语言的那点小事

这篇文章主要讲讲本人近期对编程语言的理解,对编程语言涉及到的编程方法(范式)上的差异以及产生这种差异的内在原因的理解。这次用来做示范的是当前普遍被使用的两门编程语言java、python。之所以以这两种语言作为参考,是因为这两种语言恰恰能够代表编程语言的两个主要类别,那就是以java为代表的静态语言和以python为代表的动态语言。静态语言家族包括C/C++、C#、java、go等,动态语言则包括有python、javascript、php等。从规模上可以看出,这两种类型的语言都占有着非常大的使用比例,流行程度上难分高小,应用领域都非常广泛。由于时间有限,仅就java和python两种语言作为各自的代表进行研究。有不对之处,还请不吝指教。

  • 静态语言 or 动态语言

怎么区分静态语言还是动态语言?其实很简单,先给出一个浅显易懂的判断标准:定义变量时,需要声明变量类型的是静态语言,不需要声明的则是动态语言。是不是很简单?那么有人要说了,你前面提到的C#、go语言,甚至连新版(JDK9)的java语言中都可以如示例1这样定义变量,这难道说明它们都是动态语言,而并非你之前提到的静态语言咯?

var value = 100; //示例1

确实,按照上文给出的简单方法,则C#、go语言等也许都可以划入动态语言阵营了。然而,实则不然,伴随着人们对动态特性便利性的重视,很多语言与时俱进,都引入了动态语言的部分特性,连java这种老气横秋的语言都在与时俱进:)。在此,需要给出更加准确的定义才能区分。我没有参考教科书或者任何官方解释,仅个人见解的描述。

静态语言是指在编译期能够确定变量类型的语言,而动态语言则是指在运行期间才能确定变量类型的语言。

利用这个定义就可以正确判断到底谁是静态语言,谁是动态语言了。示例1中的代码如果是C#,那么value的类型其实是在编译期确定的,这是利用编译器的类型推导能力实现的,即通过右值的类型推导出左值value的类型(有兴趣研究类型推导的同志可以关注下类型系统这个领域),所以C#还是只能归为静态语言一类。而如果示例1是javascript的代码,则value的类型是在运行期方能确定,因此javascript是动态语言。这样我们就终于能够区分清楚了。

很多同志在判断静态和动态语言的时候,经常会被另一个概念所迷惑,那就是强类型和弱类型。他们往往认为静态语言就是强类型语言,而动态语言就是弱类型语言。这是一个严重错误的认识。其实这个强弱类型和动静态是两类不相干的概念。比如作为动态语言的python是强类型的,因为python的类型不能相互隐式转换,而作为静态语言的C/C++语言则是弱类型的,因为C/C++语言允许类型进行隐式转换(如int转double)。具体哪些语言属于什么类型可以见下图(图来自网络),大家可以自己思考一下为什么java是强类型语言。

编程语言分类图
  • 用python和java编程的“感觉”

相信写过两种语言的同志们都会有各自对两类语言编程风格的独特体会,从我个人而言,概括起来,那就是:

python(代表动态语言):自由灵活,却神锋太俊,飘飘乎云端。
java(代表静态语言):严谨务实,冗长无趣,胶柱鼓瑟,自缚手脚。

java作为一门工业化级别的语言,适合团队化开发,由此诞生的各种设计模式,大大的提高了工业化生产的效率。设计模式就如同一家大企业的各种制度,一定程度的缓解了大公司体量所带来的负担。但同时也限制了员工们个人的创造力,减弱了他们认知的广度以及尝试新鲜事物的好奇心。而python的动态化特性就完全放开了各类约束,鼓励你放飞思想,写出简短精悍、富有想象力的代码。但也正是python的这种特点,造成了其不适合团队化、大规模合作的生产环境,由于缺乏编译期类型检查(虽然有扩展支持类型检查工具,用的很少),将错误推后到运行期,导致编译器工具无法对其进行静态代码检查和优化,加剧了调试和测试的需求。且风格过于灵活,需要辅之以大量的编程规范,加大了工作难度。

  • 设计模式的围城

每一个java程序员都会对设计模式津津乐道,建立在设计模式之上的各类“框架”是java生产力的重要支撑,脱离了各种方便的java框架,或许很多java程序员瞬间就会无所适从,双手一摊,表示无能为力了。可见,设计模式在java语言的应用中占有非常重要的地位。举个例子,Spring是一个家喻户晓的j2ee框架,里面最核心的依赖注入和控制反转的理念就是设计模式思想的体现。java反射的语言特性为Spring实现控制反转的技术机制,而工厂模式则为依赖注入提供了思想源泉。同理Spring对AOP的支持,则是代理模式的应用。这些一定程度上体现出了设计模式的威力。

那么python程序员需要设计模式吗? 让我们来仔细思考一下,不妨用python实现一个工厂模式和抽象工厂模式看看:

class Human:
    pass

class Woman(Human):
    pass

class Man(Human):
    def a(self):
        return 100

factory = {
    "man": Man,
    "woman": Woman,
    "human": Human
}

class Chinese:
    pass

class European:
    pass

class Dog:
    pass

class Pig:
    pass

#工厂就这么简单
man = factory["man"]()
woman = factory["woman"]()
human = factory["human"]()


manFactory = {
    "chinese": Chinese,
    "european": European
}

animalFactory = {
    "dog": Dog,
    "pig": Pig
}
    
#抽象工厂(无非就是工厂的工厂)
abstractFactory = {
    "manFactory": manFactory,
    "animalFactory": animalFactory
}

...

我们看到了动态类型的威力,自由灵活。在这个例子当中,没有涉及到java中的所谓接口、继承、实现等实现工厂模式必须的概念。其实,设计模式很大程度上是为了弥补java语言在语言动态特性不足上的缺陷,大量的设计模式相当于是给java语言打补丁。而python则在很大程度上讲设计模式的思想内置到了语言的内涵当中,这其中的二三事需要仔细体会。下面再举一个策略模式的例子(也许是个悲伤的故事):


def loveMeOrHim():
    return input("love me or him? ")

def loveMe():
    print 'love you too!'

def loveHim():
    print 'my best wishes'

def loveNoOne():
    print 'all right, we can be good friends'

def firstStrategy(*arg, **argw):
    loveMe(arg,  argw)

def secondStrategy(*arg, **argw):
    loveHim(arg, argw)

def thirdStrategy(*arg, **argw):
    loveNoOne(arg, argw)

#由于函数或类在python中都是first-class的,所以策略模式就是这么简单
def main():
    girlsWord = loveMeOrHim()
    if girlsWord == 'you':
        solver = firstStrategy
    elif girlsWord == 'him':
        solver = secondStrategy
    else:
        solver = thirdStrategy
    
    solver()

main()

为什么这里实现策略模式很简单又很直观呢?脱离了java的接口、实现、继承等概念。原因就是python语言将函数或类作为了first-class的结构,可以理解为函数和类本身就是一个值,这个值可以被传来传去,可以被当做参数赋来赋去。可以换句话说,python语言直接就内置策略模式,或者说至少内化了策略模式的思想。读者同志们如果感兴趣,可以将java常见的各种设计模式用python实现一遍,记住不要一来就设计什么接口啊、类啊,最好透过现象看本质,先抛弃各种java强加于你的思维定式,再充分利用python语言本身的特性来设计实现(熟悉python的同志都知道,用python实现个AOP又是何其的简单),如果你能够剥去浮在表面的各种尘埃,还能推陈出新,则几近道矣。

  • 结语

其实还有很多想说想写的话,但是我是一个没有耐心的人,一时心血来潮毕竟无法持久的输出,恰如王献之雪夜访友。我对编程的兴趣也是来去如风,不萦于怀的,兴尽则收,或许也是败笔之源。其实这篇文章通篇废话,俱是基础,也就供些年轻人笑读尔。回想韶华不驻,二十六载一晃即过,所见诸事本皆小道所宿尔,不提也罢,怎可谓几近道矣,羞而大笑。

你可能感兴趣的:(语言的那点小事)