注:此文对应于孟岩先生的博文(http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx)。似乎无法大段回复,所以直接贴在这里。急救之作,希望孟老大能包涵。回头再细细润色。根据自己的理解和经验,提供另一种思考。不幸的是,和孟先生的观点有全面冲突。希望冲突之下可以有更深的讨论出来。
1. 技术和工具。
OO技术中,最核心的是使用类来表达概念。亦即,我们可以操作的元素的粒度不仅仅是一门具体的语言提供的基本元素了。对于过程式编程,你能操作的,用来构成语句的基本元素是基础类型,逻辑条件以及(进一步的)函数名字。当你拿着锤子(语言提供的基础元素)的时候,你看到的都是钉子(基础元素的组合。源于基础元素的属性和可支持的操作,这些组合都是低级的,其变化也是有限的)。
而OO编程中,你可以操作的元素可以是实现了任何功能的完整封装的对象。你的作用是(理想地)按照业务逻辑的要求组合调用这些对象。这才是OO之所以有用并且得以维持的原因。重申一下:OO的重要性在它提供可以更好地构建更大粒度的可操作元素。(至于为什么看起来简单的进步会导致巨大的结果,个人认为是和“地球人”的思维模式有关联,有机会专门讨论)。
OO在实践中,又细分为基于对象的和“面向”对象的。个人认为其区分仅仅是在使用层次,而不是OO技术上。基于对象的可以认为是OO的初级阶段,而面向对象可以粗略认为是OO技术使用的高级阶段。然而,基于对象的使用方式更好地表征了上面提到的大粒度特性;而面向对象的方式则更好地体现了OO发展带来的真正技术进步,接口和实现的彻底分开。关于接口和实现这部分,在第二部分我们讨论。现在回到技术和工具的讨论上,OO技术的核心仅仅包含以上两点,其他所有的派生特性都不过是工具而已。因为是基本技术决定了不同的工具。
过程式编程不应该和OO以及函数式编程并列,相反,过程是应该是后二者的基础。OO强调对象关联,而函数式编程强调对象的不变性(简单区分可变对象和不变对象是另一个简单的技术产生巨大效果的例子)。过程式编程(顺序执行和跳转执行)则是其基础。
然后对比孟老大的观点:我们就知道所谓的三要素以及对象间消息的传递方式不过是具体的OO工具罢了。以此来责难OO技术,也就是缘木求鱼了。
在“OO思想在不同语言的实现”一节中,会专门讨论消息传递方式。你就会明白,这种工具上的差别导致的结果其实是非常容易弥补和融合的,如果需要的话。
Alex Stepanov对OO的批评,纯粹是对OO工具滥用导致的复杂性的批评,而不是OO技术本身。在Element of Programming中,到处可以看到OO思想的影子。特别是对类和型的严格区分定义。这是下一节要讨论的。
2. 型和类的分离
在讨论OO时,混杂使用型和类是最大的问题。过程式编程不区分这一点,这可能是最根本的原因。简单地说,型就是定义一个对象可以
干什么,而类是定义一个对象是如何干的。可能你很快就明白了,这不就是接口和实现的关系嘛。确实如此。不过,接口和实现仅仅是表现,也就是说,这是实现型和类的工具。对于基本类型,如C中的int类型,其型包含一切C语言标准有着良好定义的可以针对int类型的操作,其类则是C编译器实现中int类型实例的表达,一般对应于一个机器字长。
由于在具体的使用上,我们是针对具体对象的,而不是型或者类,其差别根本无从体现。然而在OO技术中,我们已经知道,我们只关心对象的型。这就提供了一个空间,我们可以透明地替换型下面的实现。根据语言提供的机制的不同,如何替换型的实现也迥异。《设计模式》是面向对象技术的高阶应用。当初学习的时候,一个最大的问题是,为什么向诸如工厂方法这样的模式也需要使用继承?不就是简单的对对象创建过程的封装嘛!直到彻底理解型和类的分离之后,才明白,这是在类似C++这样的静态强类型语言中是必须的,因为继承是实现型和类分离的唯一工具。C++中,型对应的概念是抽象虚基类,而类对应的概念就是非虚拟class。第四节我们详细列出C++怎么实现OO。
这样就很清楚了,孟老大对静态系统的批评完全指错了方向。第一,静态类型系统和OO无关,这只是实现OO的一种选择,不过恰好C++选择了而已。静态类型系统的作用是经可能使得运行时错误可以在编译期发现,这样程序运行的效率就可以大大提高;第二,即使是动态类型系统,型的信息同样是存在的,也是使用相对固定的定义对花花世界建模。
静态消息模型和动态消息模型不是对立的。GoF给出几个经典的模式用于构建动态消息模型,这是稍微复杂一点的系统的常规手段,所以说静态对象模型扭曲了什么的批评是没有道理的。我承认这里存在因为概念的混淆而导致的理解错误,这是因为我们都在学习过程中。
在GUI系统,使用OO技术构建后,虚函数的开销最多不过几百个虚函数(K的量级),而一个随便的GUI图标就是几十K,甚至上百K。上量级的差别让这点开销显得微不足都。更重要的是,动态系统中尽管可以避免这点开销,但是虚函数的调用却要话更多的时间。这里就是简单的时间和空间的权衡而已。
3. 名词的准确定义和使用
上面几节中,我们已经谈到各种不同的概念。不能准确地定义我们讨论中涉及到的每一个词汇,就相当于把中文直接交给C编译器编译。这是为什么稍微正式的技术书籍都会在书末尾给出术语定义的原因,也是为什么GoF专门强调模式的名字重要性的原因。这里先不给出确切的定义(很难,只能是描述),但是准确地理解它们非常重要,非常重要!
基本概念
接口:对象可以处理的消息类,消息参数,可能包含返回型
型:接口集合
类:型的实现,一个类可以实现一个型,也可以实现多个型。
对象:类的表达。每个对象都有唯一的标识。本地对象的标识可以是内存地址,远程对象的标识可以是url等。一个对象可以使用一个其实现的型(的指针)来表达。
OO:使用型来表达大粒度的概念,并且通过接口相互调用。
扩展概念
继承:一种在诸如C++语言中使用,实现特定型的方式
抽象:从业务逻辑中提取型的过程
多态:使用型来表达对象时候,根据其表达的实际对象的不同,在运行时产生不同行为的现象
设计模式:一组约定的,可以在特定情况下产生预期效果的型,类和对象组合方式,具有特定的名字
4. OO思想及其在不同语言中的实现
OO思想影响到每一种现代语言。这里仅仅描述几种风格迥异语言是如何支持OO的,这几种语言是C++,Perl以及javaScript。对于C++,大家都很熟悉。下面这个表格C++是如何支持OO技术以及常用的工具的,括号中的描述。
Type and class
Interface: the message it can handle (Virtual function in abstract class)
Type: interface set (Abstract class)
Class: type implementation (Concrete class)
A class may have(support) more types (Multiple inheritance class)
Messaging
The sender (None or implicit this object)
The receiver (Named object)
The message (Named member method)
The message body (Parameters of member function)
Inheritance relationship
“is-a”? (Public inheritance from class)
“has-a”? (Private inheritance or object composition)
“implement-a”? (Public inheritance from type)
Abstraction
Bind data and behaviors together (Member variables and methods)
Hide as more as possible information (Access modifier)
Perl本身是不支持OO的,但是通过对核心数据结构的额外扩展以及神奇的bless语句。Perl号称也是OO语言了。Perl是这样做的:
Type and class
Interface: the message it can handle (Not available, always implemented as function in package)
Type: interface set (functions defined in package)
Class: type implementation (package)
A class may have(support) more types (@ISA syatax, list all its super classes)
Messaging
The sender (None)
The receiver (Named variable that returns from package constructor function, named new usually)
The message (Function name of the package)
The message body (Parameters of member function)
Inheritance relationship
“is-a”? (similar to "implement-a")
“has-a”? (object reference to child)
“implement-a”? (@ISA syntax)
Abstraction
Bind data and behaviors together (bless class name and anonymous reference to array, hash etc together)
Hide as more as possible information (all are public visible)
可以看到,perl中对OO技术的支持不是很直观,而且不能有效区分类和型。但是,把所有的成员变量和成员函数作为数据以散列的形式存储起来并且绑定到具体的对象的方式是一切动态对象系统的雏形。
JavaScript是所谓的纯OO语言。它是怎么支持OO技术已工具的呢?看下表。
TODO
5. Damn it! C++
最终到了最后。有人以为我会为C++摇旗呐喊,为C++“正名”,为C++“讨回公道”,其实不然,凭借C++对编程语言的巨大影响,根本无需我等置喙。对于据大多数C++程序员来说,对C++是又爱又狠;完全掌握这门语言更是被认为不可能(包括Dr. Stroustrup)。然而,C++本身的发展导致的成果已经在其他多种新的语言中得到了应用。有人认为C++会最终被自己的自重压垮。尽管我也稍这个担心,但是却更具信心。C++是越来越易于使用就是明证。伴随着C++社区整体技能的提升,可以预见更好的库会越来越多,这会使得C++越来越适宜不同的开发领域,并且越来越容易。容易最后的结果就是在一个完善的概念系统下的简单!不经历复杂不可能简单,正向老爱说的,要经可能简单,而不是简单化(Everything must be made as simple as possible, and not simpler)。
并不是所有的C++特性你都要用的!只深入学习你需要的那部分即可(感谢C++不需要就不付出的设计哲学)。有时候看到有人说他们把项目从C++转回到C了,祝他们好运。他们遇到的问题其实和C++本身没有什么关系。个人角度,使用C++就是建立一个的C++特性的合理子集,坚持并且一致地使用它们。我能想象到的这些技术和特性包括:
资源管理技术(基于RAII以及引用计数的智能指针,在C++社区已经成为共识)
错误处理技术(基于异常的错误管里,尚无一致接受的最佳实践,讨论很多,批评更多)
标准库
合理使用模板
其他更能表达语义的特性(const, *_cast)
其他更能提升生产率的特性(C++0x)