编写领域语言

原文链接

两个代码库。其中一个是这样的:

1
2
if (portfolioIdsByTraderId.get(trader.getId())
.containsKey(portfolio.getId())) {...}

你抓破头皮,想要搞懂这段代码要做什么。看起来像是从trader对象中取出一个ID,通过一张表查找另一张表,好吧,上面代码就是表中表的玩法,但好像又是在比较portfolio对象是否存在这张表中。哎呀,抓耳挠腮呀。继续从portfolioIdsByTraderId中寻找线索:

1
Map<int, Map<int, int>> portfolioIdsByTraderId;

渐渐的你发现,这应该和trader对象是否要访问某个特定portfolio对象有关。毫无疑问,你会发现更多相同的代码片段,或者更多看着像但很隐蔽的不同的代码片段——不论什么时候都要注意trader是否在访问某个特殊portfolio。

在另一个代码库:

1
if (trader.canView(portfolio)) {...}

这次不用挠头了。有不需要知道trader内部在干嘛。或许里边就有藏着各种表。但那是trader的业务,和你没关系。

现在,你更希望用哪个代码库完成工作?

从前我们只有很基础的数据结构:bit和char(当然还有bytes,我们假装用于字母和标点符号)。小数点比较棘手,因为我们的10个数字在二进制中不好弄,所以我们又为浮点类型扩充了一些内存长度。这次有了array数组和string(其实就是不同的数组)。然后我们又增加了堆栈、队列、哈希、链表、跳表以及大量的根本不存在真实世界的数据结构。“计算机科学”就是花大量的时间把真实世界映射到我们限定的数据结构中。真正的大师甚至能够记住他们是如何做到的。

我们有了user类型定义!OK,这不是什么新闻,但它的确改变了游戏规则。如果你的业务领域包含像traders和portfolios的概念,你可以对它们建模,称之为,Trader和Portfolio。但比这更重要的,你也要会给它们两者之间的关系进行建模。

如果你不用领域术语编写代码,而是创建一个心照不宣(佛曰:不可说)的未知事物,就会出现同样是int,在这里表示trader,跑到那里有代表portfolio。最好是不要将它们混淆!如果你直接通过一套套算法去表达某种键表的存在关系(比如,“一些trader禁止去查看一些portfolios”——属于非法行为),你并没有给审计人员带来任何好处。

下次到来的程序员又不懂里边有什么猫腻,所以为何不直接把它表述清楚呢?用一个键去检查另一个键是否存在,太模糊了吧!怎能让其他人直观地理解,实现“利益冲突”的业务规则到底他妈的在哪?

将一个领域概念清晰地在代码中表达出来,意味着其他人可以更容易地理解领域并在对应算法的地方加以改进。这也意味着如果一个领域模型需要迭代了——你对领域理解更深的时候,你可以更好地定位到对应的代码。加上良好的封装,业务规则很可能只会存在一个地方,你可以不依赖任务其他代码,直接修改它。

下个到来的程序员会因为只花少量的时间就进入工作而感激你。下个到来的程序员也许就想成为你这样的人。

0%