Julia语言程序设计
上QQ阅读APP看书,第一时间看更新

2.3 经典编程范式

面对需要程序化的实际业务需求时,往往要考虑很多问题。例如:

·使用怎样的解决方案?

·如何对问题进行分解、抽象?

·如何对问题所属的各种概念和业务逻辑进行构架?

·如何组织代码,又使用怎样的数据结构?

·如何建立高效、低耦合、灵活的接口?

要解决这些问题,需要我们对业务有着深刻的理解,也考验着开发者在程序设计方面的经验与能力。不过这个阶段也是最有挑战性的阶段,充满了各种乐趣。

看待这些问题的角度和思维方式,构成了所谓的编程范式以及设计模式,也是我们在程序设计中长期积累出的哲学思考与智慧。这些在大量开发实践中总结出的经验,能够让我们在面临各种纷繁芜杂的程序设计工作时,选择最为恰当的技术方案,保证代码的灵活性、兼容性以及运行效率。

不同的范式,对最终的代码呈现和程序运行效能等方面都会产生影响。有的程序具有更高的运行效率,有的能够更灵活地适应业务逻辑的不断变化,有的在模块化、分层化、接口化方面更为方便,有的在代码复用、开发与维护成本上更高效。

在语言层面,有几种经典的范式:面向过程编程、面向对象编程、泛型编程和函数式编程等。有些语言只支持一种范式,有些则支持多种范式。不同的业务需求,可以选择适用的语言,使用合适的范式,甚至可以混用。

1.面向过程编程

面对一个问题,最直接的方式是将解决流程罗列,并以代码逐句实现。这种线性处理过程,便是面向过程编程。该概念的明确表述始于1965年的E.W.Dijikstra,对当时来说是软件发展的一个里程碑。

面向过程的观点是任何程序都可以使用顺序、选择(if-else)、循环(for/while)这三个基本结构进行控制,并可通过函数的形式对步骤进行局部封装与调用,所以也被称为结构化程序设计。例如,C语言是典型的面向过程语言,虽然古老但至今仍有不可替代的用武之地。

在面向过程中,在实现顺序时,逢山开路,遇水搭桥,路一直往前铺。交代机器做事时,不会存在动作的主体,也不会存在业务主体,关注的是各种动作及其顺序;只需告诉机器先这样、再那样、然后再这样,最后进行输出。

2.面向对象编程

面向对象设计向现实世界靠近了一步,有着与面向过程设计完全不同的设计思路,是软件工程与计算机语言发展的又一里程碑。这种编程范式思考的起点在于业务逻辑中的主体,有了更多、更高层次的抽象,体现了一种独特的世界观。

在面向对象的实施过程中,需先将各种动作或过程进行梳理、综合,抽象出各种交互主体并建立相应的一套事物模型(即对象);而其中涉及的步骤和动作,则被剥离为这些对象之间的交互行为或者主体状态的变更操作,体现了数据的转移变换过程。

基于此,在包括C++与Java的经典面向对象语言中,出现了常见的类、对象、封装、继承、多态等基础设施或概念。

对象是动作的实体,是逻辑的实际执行者;而类则是一种抽象概念,是对具有同样特点对象的描述。在面向对象的语言中,会将资源与动作进行封装,并赋予各种操作权限,定义出类结构。但类只是规则的集合,并不拥有实际资源,只说明同类对象具有哪些属性数据,具备怎样的行为,可以执行哪些操作,如何与外部交互。对象是类的实例化,会在运行期被分配到具体的内存区中,从而实现实际的操作或动作。

但现实世界是复杂多样的,事物之间往往存在着各种关联,也会有不同的概念层次。例如颜色与黄色、车与汽车等,前者与后者便存在着包含关系,在面向对象语言中对应的则是继承关系。通过定义抽象类,将“车”一类共有的属性或行为封装,例如启动、移动、停止等,然后再定义“汽车”、“平板车”等子类继承该抽象类,便可实现代码复用与层次控制。

但任何独立存在的事物都有其独特性,虽然都是车,但在形态、轮数、驱动方式等方面都有所差异。为此,在同属抽象类的子类中,相同的动作会需要不同的控制参数,也需要不同的具体实现。在实际执行时,虽然通过同一个动作名(函数名)执行相应的操作,但却能够有不同的表现,这便是多态。

但是在实例化的过程中,有些类不会出现多个对象,例如,某个共享的计算资源或服务,某个设备或端口,或者是通用的计算逻辑等。为了对这些单一的、仅需一份的对象进行安全的控制,可在实例化时创建类的单例,这也是设计模式中惯用的方式。

设计模式是编程经验的高度总结,在抽象层次的控制、结构关系解耦等方面给出了很多可供遵循并能落实的经典范例。

3.泛型编程

“类”在面向对象语言中是一种类型,封装了共同的属性、数据和动作。但有时候,不同的类型也有着相同的动作甚至执行过程,例如,人步行的行走路线和车行驶的路线,雨或雪下落时的物理过程等。

对于这种不同类的相同动作,重复实现过程确实是没有必要的。为此,泛型编程将常用的、统一的处理过程抽象出来,并能够适用于不同的类型,不但能最大限度地实现代码复用,而且逻辑的集中也为开发和维护带来了巨大的好处。不仅如此,在泛型编程中类型本身也可以作为参数,能与其他参数一起共同控制处理逻辑的实际运行。

泛型编程最初诞生于C++语言,由Alexander Stepanov和David Musser提出,代表作是C++的标准模板库(Standard Template Library,STL)。C++泛型编程之所以能在工业上取得巨大成功,得益于其高效性和通用性。

Julia语言极大程度地借鉴了泛型编程的优点,并提供了完备的机制,其灵活的类型系统与类型参数化,都体现了泛型编程的强大魅力。

4.函数式编程

函数式编程是计算机语言在另一个层次的发展。在该范式中,函数结构不再只是执行过程的集成,而成为了类型或对象的一种。尤其在Lisp这样的函数式语言中,函数更是逻辑的主体和基础。

Lisp语言是典型的函数式语言,于1958年由麻省理工学院的人工智能研究先驱John McCarthy发明。在以Lambda表达式及λ演算(Lambda Calculus)为基础的Lisp语言中,函数作为对象,也称为闭包(Closure)或仿函数、函子(Functor),除了可被调用外,还可以赋值、作为参数与返回值,甚至是传递转移。这种高阶函数的设计使得函数式编程具备了很多优点:简洁的代码、快速的开发、自然的表达和易于并发等。

Julia语言同样借鉴了函数式编程的优点,不但函数是其基本的对象,类型本身、甚至代码本身都是可以操作的对象。可以说,“一切皆对象”是Julia最为吸引人的特色之一,使我们能够在Julia编程中灵活地选择想要的范式,从而适应各种不同的需求。

不过在实践中,并没有最好的范式或方案,适用的便是最好的。在合理的时间内,开发出可用、高效、稳定、可靠的程序才是唯一的检验标准与范式。