在之前的文章中,我时不时会使用主客体这个概念来分析和说明一些问题,由于它足够的简单,所以都是放到具体的场景中描述的。有一些朋友告诉我说,通过主客体这个思维想通了非常多的事情,尤其是放下了对 DDD 中一些过于理想化的执念。虽然大多是都谈论 DDD 的时候附带谈到它,但是好像能解决一些超出 DDD 范畴的问题。
今天就来简单聊下主体和客体这两个概念本身,并且结合 ”建模“ 来讨论怎么对我们日常软件开发带来帮助。由于这几个概念非常的简洁,加上最近也比较忙,因此这篇文章也不会太长。
01. 关于主体和客体的概念
主体和客体两个概念来源于哲学分支的认识论,我最早了解到它是通过黑格尔的《小逻辑》,它的发展伴随着哲学历史的发展。和软件工程中的各种思想一样,人们在认识世界的过程中也有不同的思想和观念,这些观念由于每个人的语言体系不一样导致概念的层次混乱。
所以软件开发者对如何构建软件这件事情上的争论,和历史上哲学家对世界观的争论非常类似,直到哲学体系的建立,才让人类在世界观上能使用同一套术语进行讨论。哲学和面向对象方法学一样,它的门槛极低,以至于每个人都可以说上几句。但是它的上限又极高,较少人能形成自己的世界观和方法体系,而不在不同的语言体系下迷失方向。
主体和客体这两个概念正是哲学的精华,而面向对象刚好是一个在计算机编程中对现实世界恰当的比喻,把这两个概念引入软件开发实在合适不过了。
这两个概念用简单的话来说就是:主体(Subject)是行为的实施者,客体是行为的承受者(Object)。
可能你会觉得这有啥,我小学就知道了。其实我们现在很容易理解这两句话,这是因为历史上的哲学家做了大量的工作,然后现代社会通过基础教育潜移默化的传递给了我们。以至于我们把这种思维方式当做理所当然,以至于错过了更为深刻的思考。
在解答朋友关于 DDD 充血模型的困惑时,就能说明为什么它如此简单却又如此深刻。在面向对象的思维中,我们使用对象来封装行为和数据,这非常合理,但往往让人把握不住。
鸟会飞,车会跑,订单会结账。
这听起来理所当然,朋友在阐述充血模型时,觉得业务逻辑应该被封装到订单中,但是在编码时却感觉到非常不适宜。
在现实中,我们去餐厅吃饭时,结账的是收银员,被结账的对象是订单。既然我们在讨论面向对象的时候,往往是带入现实世界的认知开始的,为什么会觉得让订单能自己结账是理所当然呢?
如果把构建软件的开发者比喻成上帝的话,这样做显然是不合格的。
因此我们可以得出主客体建模方法的第一个规律(朋友建议叫做第一定律,但是这样显得傲慢):
在一个行为中,主客体必然存在。
那么,可能会有读者朋友反驳到,我可以把所有的数据传给订单,然后也能做完所有的工作。
是的,这个时候订单这个对象被赋予了两个身份,作为客体承载订单这个虚拟物品的数据和系统状态,作为主体去操作订单上面的数据和方法的参数。订单操作订单上的订单项时并生成总价时,主体是订单,那么客体就变成了订单上的属性了,构成了另外一个局部的主客体关系。
这就是主客体建模法的第二个规律:
主体或者客体可以嵌套存在。
利用这个规律,可以澄清在软件开发过程中很多问题。比如当用户使用一个软件的时候,软件本身作为客体,使用软件的用户为主体。但是在开发者眼里,软件内部的代码中有 UserService、User Model。如果用户使用软件并注册了一个用户,那么就构成了一大一小两个主客关系:
- 使用软件的用户作为主体,客体是软件
- UserService 是主体,User Model 是客体
这样看容易混淆的几个概念就被澄清了。
但是想做上帝没那么容易,还有一个悖论等着。我们试着想象有一个充血模型的代码,订单需要批量结账,因此传入一个订单数组,那么令人迷惑的是,负责处理这个订单数组的订单对象是什么呢,这个对象可以出现在这个订单数组中吗?
同理,一个人无论使用多大的力气,也无法把自己举起来;上帝可以创造万物,也不能创造自己,即使能创造另一个上帝,这个新的上帝还是他自己吗?
这就是主客体建模法的第三个规律:
一个实体不能在同一个行为中,既是主体也是客体。
通过这个规律能避免很多不恰当的设计,因为这些设计会让人非常迷惑。
其实在日常生活中关于这三个规律无处不在。在学习英语的时候,英语语法的主谓宾结构,就是一种主客体思维;在使用法律为自己争取合法权益的时候,我们需要明确我们的诉讼对象,也就是法律关系中的主体。
02. 关于建模的延伸
在做培训或者演讲的时候,有朋友会问?到底什么是模型。有个朋友用四川话描述比较幽默:
”你们天天说的建模,到底说的啥子嘛,虚头巴脑的,人家街(gai)上卖的馍馍,你一听也晓得是圆的撒。“
他一边说,一遍用两个手的大拇指和食指,对我做了一个圆的手势。我见状就说,你现在就建了一个模。他更不解了:
”我啥也没做,你是不是在哄我哦“
其实我并没有故弄玄虚,他用手势比喻 ”馍馍“ 是圆的,这不就是我们说的模型吗?
也刚好能说明,模型只是一个比喻当不得真。
老板使用 PPT 给产品经理讲解某个软件的商业逻辑,使用了一些图表来表示运作的原理,这是一种商业模型。
产品经理使用 Axure 绘制软件的线框图,这是一种业务模型。
开发人员通过分析业务模型,绘制了 UML、E-R 图来表达这些业务概念在软件中的形态,这是软件开发中的模型。
甚至代码也只是用于程序员表达业务的一种模型,真正被编译后的二进制文件才是软件本身。
模型是一种比喻,当不得真。
正是因为模型是假的,无论用分析模型、代码模型、业务模型等各种名词去称呼它都没有对错,争论它们叫什么也没有意义。
开发人员、架构师理解了这一句话后才开始慢慢放下对模型的执念,放下争论,重头去思考软件应该如何构建才更为合适。
在建模的范畴下,我们追求概念共识,模型合适,比追求对错更为有意义。
03. 总结一下
把主客体引入软件建模后,并没有什么值得学习的,而是需要在各种实践中使用即可,这些内容会在后续的文章中慢慢聊开。
为了能系统使用和记忆,我把这套东西整理成了一个套路,叫做:1 原则,2 要素, 3 规律,多应用。
1 原则:模型当不得真。 用辩证的眼光看待,追求共识大于对错;追求取舍权衡大于绝对理想。
2 要素:主体(Subject)是行为的实施者,客体是行为的承受者(Object)。
3 规律:1. 在一个行为中,主客体必然存在。2. 主体或者客体可以嵌套存在。3. 一个实体不能在同一个行为中,既是主体也是客体。
在应用方面就非常广阔了,比如:通过找到业务主体来分析谁是软件真正的用户;通过寻找业务客体构建领域模型;通过定义代码结构中每层能力来找到能力主体;通过维护 IT 资产的责任主体来明确团队边界。