面向对象编程中的「对称性」

The flexibility provided by Smalltalk’s high degree of symmetry and extreme late binding have proven to help programmer productivity and creativity more than any theoretical benefits that might be derived from the use of static type checking, symmetry-breaking primitive data types or symmetry-breaking syntactic sugar.
(译文:Smalltalk 高度的对称性和极致的延迟(动态)绑定特性所带来的灵活性,帮助提升程序开发人员的工作效率和创造力。这种收益已被证明高于任何静态类型检查,对称性破缺的基础数据类型或者对称性破缺的语法糖所可能带来的理论收益。)

对称性的概念

对称性在几何和代数中有不同的定义。几何中,对称性是指对几何形体施加某种操作使它的位置能完全复原。代数中,对称性被描述为给定的组合定律下闭合,关联和可逆的一组变换。不论哪种功能定义我们都可以得出,对称性的核心即是变换条件下的不变性

分类(classification)与对称性(symmetry)

从认知科学的角度讲,我们人类的思考基于对事物进行分类的能力,分类最终建立起类(class)和实例(instance)的关联。一些该领域的人甚至认为分类支撑了人类的认知,并帮助构建了人类记忆存储的组织结构。在编程领域,面向对象编程语言通常支持两个层次的分类,即对象归类构建类之间的结构关系。基于此,有人认为所有面向对象的概念都可被统一于某种理论模型,并由分类学作解释。这也就提出从一种新的视角,即从生物学(biology)和分类学(taxonomy)的角度,来理解面向对象编程。

面向对象语言中,类是对象的分类。这种分类直接构建起了类的不变性——类的描述适用于该类的所有对象。从这个角度进一步推导,类便拥有了对称性:类赋予了对象多变性,但这种多变性需要遵循该类所定义的结构和行为。类的对称性带来的好处是,它为对象的多变性划分界限,且强制性地保证了类的正确性。

类的概念在基于组件的软件开发中非常有价值。组件可以按照如接口或者行为的兼容性等共有特征,分组为不同的类。于是类就能够用于创建具有共同特征,但各自不同的组件。当应用的需求发生变化时,只要新的组件遵循该组件类的共有特性,那么它就可以用于替换旧的组件。这种可替换性对基于组件的软件开发很有意义。另外,软件维护和演进过程中,替换过时或问题频出的组件已然令人头大的情况下,组件的可替换性也就同样重要。

更为重要的是,对称性和分类之间的联结,为面向对象软件设计提供了理论研究的基础。通过应用对称性原则,软件设计的目的就变成了最大化地识别和保留设计上的不变,同时能良好地支持变化。

接下来我们看另一种对称性。继承(Inheritance),大多时候会和一般化(generalization)与特殊化(specialization)概念挂钩,较少有研究者将之与分类(classification)相关联。面向对象编程中,分类上下文里,继承的角色不如类那样清晰。这是因为继承机制本身的灵活性。继承可以用以扩展类,限制类,或者修改类。这就意味着,子类和其超类可能并非包含关系,并且超类的描述也可能不适用于所有其子类。因此继承并不总能将类做出分类。但是,当继承用于子类型(subtyping)时,它就可以被看作是类的分类,也就保持了子类和其超类行为的一致性,亦即行为的不变性。此处需要明确一下,子类型只是继承扮演的角色之一。例如基于原型编程中对象的继承。因此,只有在子类型的概念下,继承方才用于对类进行分类。子类型和对称性的内联表现为:所有通过子类型方式构建的类,彼此可能大不相同,但一定保持并符合某种相同行为,或者说,它们是相同类型。

当然,我们可以进一步在面向对象编程中找到其他对称性的案例。但是以上两个例子就足以说明对称性在编程中的重要性。接下来,我们换一个视角进一步来了解对称性。

对称性的威力

对应到大众熟知的「面向对象编程」语言,如 Java,C++ 和 Python,「类和继承」的确被放在了核心位置。相应带来的对称性,当然也就体现在这些编程语言中。

作为对比,观察 Smalltalk 这一面向对象编程语言会发现,类,本质上是对象。该对象本身也是另外某个类(这种类被称为类的元类,metaclass)的实例。类是一个元类的唯一实例。所有的元类都是类 Metaclass 的实例。Metaclass 类是「Metaclass class」的实例,而后者又是 Metaclass 的实例。如此递归。Smalltalk 中类的编程只是一些操作数据的程序(program),这些程序本身又是数据(data)——可被其他程序操作。这不同于 Lisp 哲学(Lisp 因使用符号表达式,而让程序即为数据),但殊途同归。

甚至可以更进一步地说,Smalltalk 的对象/类系统的真正威力,不是它的继承特性,而是它的反射(reflection)机制。Smalltalk 运行在它被编写创造出来的上下文中(runs in same context it’s written in)。具体来说,Smalltalk 的运行时系统的元对象(metaobject)可以被具象化为普通的对象,被用以查询甚至查看内部细节。在 Smalltalk 中,元对象可以是类、元类、方法字典、编译过的方法、运行时栈,等等。

 

reflection

 

我们看到,Smalltalk 中体现出了上文所没有涉及到的另一个层次的对称——程序和数据的对称,数据即程序,程序即数据。这种对称在诸如 Java、C++ 和 Python 等面向对象语言中并不存在。甚至,Smalltalk 中体现出的这种独有的对称性,让其「类和继承」特性带来的对称性黯然失色,以至于类的继承仅仅是代码重用的方式。这种数据即程序的对称性,意味着,所有的信息都封装在即时运行的活(living)对象中。它使得 Smalltalk 调试器在程序执行过程中能够暂停,剖析,修改和恢复。这也是为什么 Smalltalk IDE 不仅仅是使用 Smalltalk 编写的,它也是 Smalltalk 语言本身。

后记

在查阅「面向对象」概念相关的文章时,数次出现了「对称(symmetry)」 这个词。让我疑惑的是,编程语言和对称有什么关系?进一步地调查发现,对称性的概念远远超越了设计范围,广泛存在于多种理论中。归根结底,对称性表现出来的哲学意义极具普遍性和指导性。当「变换条件下的不变性」这一个理念,和软件设计结合时,显得恰如其分。我们行业的前辈们,已然做了很多尝试,从编程语言的设计和软件开发的过程两个层面,最终想让软件开发更加轻松地应对变化。当然,这种努力今天仍旧在持续。

最后,希望本篇文章能够对大家有所启发,就像对我有启发。

 

参考文章:

Lisp, Smalltalk, and the Power of Symmetry

Understand symmetry in object-oriented languages

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s