【编程哲学 1】从“本质论(继承)”转向“功能论(接口)”
本文主要从哲学的角度来理解UML,方便记忆。
在谈这个话题前,回顾一下UML中泛化(继承)和实现(接口)
-
泛化(继承):本体论 | 实线 ───▷ | 血缘相连(实),指向抽象(▷) | 问:“你从哪里来?” Dog ───▷ Animal
-
实现(接口):功能论 | 虚线 - - -▷ | 契约约束(虚),指向抽象(▷) | 问:“你能做什么?” Repository - -- - -▷ IRepository
一、是什么与能做什么?
(1)是什么
哲学问题:
一个事物的本质是什么?即本质先于存在。
在这种思维框架下, 具体事物从第一实体那里继承某些稳定属性,因此可以归类。
例如:狗、猫、─ 鸟───▷动物
这里:狗 = 动物(一组稳定的属性) + 特殊属性 所以继承表达:个体属于某种更一般的类别。
泛化(继承)图标的标识 是什么,具体的对象从抽象的对象那里继承稳定属性的,所以用空心的▷,它和具体对象是血脉关联,因此用───实线。
但是现代软件设计发现:如果世界变化很快,“本质”可能不稳定。例如:鸟,企鹅也是鸟,但不会飞,于是继承体系的功能稳定性关系崩溃。 所以现代软件设计用契约关系、功能主义来处理属性继承问题 ,同时减少深层继承。
关键在属性的来源方向——继承的属性是从上往下"继承"来的(纵向、单一谱系、位置固定),接口的属性是可以横向叠加的(一个类可以同时实现 IFlyable、ISwimmable,没有谱系冲突)
继承(接口)关系不是问:你是什么?而是:你具备什么能力?
这是从"本质论"转向"功能论"——而这个转向,在哲学史上有一个更精确的名字:功能主义(functionalism)。
(2)能做什么
哲学上更接近:
萨特的存在决定本质。即存在先于本质,由功能来定义本质,而不是由身份(本质)定义。
普特南(Hilary Putnam)在心灵哲学中提出的功能主义(functionalism)主张:一个状态的本质由它的因果/功能角色定义,而非由承载它的物理基质定义。同一个"疼痛"状态,理论上可以在碳基大脑里实现,也可以在硅基系统里实现——这被称为多重可实现性(multiple realizability)。
这跟接口的核心思想几乎是同一件事:IFlyable 不关心你是鸟、飞机还是超人,只要你实现了 Fly(),你就满足这个契约。软件工程里"面向接口编程"这句话,在哲学上其实早就有专门的学派在讨论了。
实现(接口)图标的标识是"能做什么",是对多个具体对象共同能力关系的抽象,而不是对继承关系本身的抽象。关注对象之间共享的行为契约,如:基于共同契约约束(法律契约、技术标准、协议规范、行业认证、准入协议、文化、道德等),因此用- - - -虚线。
二、更深一层:本质先于存在,还是存在先于本质?
这个对立还可以往哲学史更早的地方推:萨特(Sartre)的存在主义命题——"存在先于本质"(l'existence précède l'essence)。
人没有一个预先设定好的本质,人是通过自己的行动来定义自己的。这几乎可以直接套用在接口思想上:
- 继承体系更接近本质先于存在:先有
Animal这个本质范畴,Dog才被归入其中,身份决定了它能做什么。 - 接口体系更接近存在先于本质:先有具体的行为承诺(实现了
Fly()),"能飞的东西"这个身份标签是后验贴上去的,行动决定了它是什么。
这也是为什么现代架构中"鸭子类型"(duck typing)、依赖注入这类设计,本质上都是把"你是谁"这个问题往后放,把"你此刻能做什么"放到前面。
三、组合优于继承:从实体本体论到关系本体论
继承是纵向的血缘链条,一个对象在这条链条上的位置一旦确定,就很难横向扩展新的身份。而现代架构大量偏好组合优于继承,这背后也有一个哲学对应:怀特海(Whitehead)的过程哲学(process philosophy)反对"实体先于关系"的传统本体论,主张事件与关系才是世界的基本单位,稳定的"实体"只是关系模式暂时凝固下来的样子。
"组合优于继承"正是把"这个对象是什么"的问题,替换成"这个对象和谁发生了关系、持有什么、依赖什么"。血缘关系是纵向的本质传递,位置一旦确定就难以更改;组合关系是横向的拼装,一个类可以同时持有多个能力模块,没有谱系冲突。
四、一个自我拷问:接口的"能力"难道不也是一种属性吗?
如果接口也是"一组稳定的属性"(能飞、能读、能写),那它和继承定义的"属性"到底有什么本质区别?
关键不在于"有没有属性",而在于属性的来源方向:
- 继承的属性是纵向继承来的——沿着单一谱系向下传递,一个类的位置在树状结构里是固定的,改变谱系代价极高。
- 接口的属性是横向叠加的——一个类可以同时实现
IFlyable、ISwimmable、IComparable,彼此之间没有位置冲突,也不需要共享祖先。
这也许比"本质论 vs 功能论"这个说法更精确地抓住了两种关系在结构上的根本差异:继承问的是"你在谱系树上的哪个位置",接口问的是"你此刻兑现了哪些契约",而契约是可以任意组合、随时增减的。
五、小结
这也是为什么现代架构大量使用:

- 接口
- 依赖注入
- 组合优于继承
因为复杂系统中:
稳定的往往不是对象的身份,而是对象之间形成的契约关系。

浙公网安备 33010602011771号