DDD 主客體命名法

關注公衆號:DDD 和微服務

微信號:linksgo2016

同名知乎:少個分號

  1. 主客體命名法

曾經我也糾結命名的問題,想使用簡單快速地方法解決,但是由於沒有意識到命名也是設計的一部分,爲此吃了不少的虧。我希望讀到這篇文章的朋友不要犯我同樣的問題,命名真的不是通過某些 ”AI“ 工具、插件能自動生成這麼簡單。

在這套方法裏面,我延續主客體思維,如果不瞭解的朋友可以先去看 《主客體建模法》。使用主客體命名法,不僅可以寫出主謂賓結構的語句,還能通過命名改善軟件設計。

主客體命名法可以做到下面這樣,代碼如詩一樣優美。

首先,我們將需要起名字對象進行分類:

對客體命名

根據 DDD 的統一語言原則,名詞往往代表着一個業務概念,並需要在團隊中和開發人員、業務人員對齊。編程就是使用特定的算法操作一組數據,這些數據代表着業務中的某些概念。

讀過《主客體建模法》的朋友可能知道我要說什麼了,這些概念就是代碼中的客體。如果想要獲得良好的命名,就需要對這些概念進行定義。

一個對象就是一個概念,對象中的屬性就是這個概念的內涵,這個對象被用來表達的範圍就是它的外延。

這裏需要普及一下邏輯學中內涵和外延。內涵是指一個概念的典型特徵,外延是指它能描述事物的集合。比如兔子有長長的耳朵是內涵,兔子在地球上指代的動物就是它的外延。

當我們說白色的兔子不是兔子的時候,說的是 “兔子” 這個概念不是 “白色兔子” 的概念;當我們說白色的兔子是兔子的時候,說的是 “白色兔子” 概念表達的集合是概念 “兔子” 表達的集合的子集。

所以對客體起名字的關鍵在於定義這個客體的概念,使用擬物的方式起名。

我們可以通過概念圖(可以搜索概念圖相關的文章)來定義,也可以直接用語言來表達。比如當我們給系統中用戶相關起名字的時候可以這樣定義:

對於容易混淆的” 地址 ",也可以這樣定義:

對主體命名

在代碼操作中操作這類客體的就是主體,那麼主體怎麼命名呢?

其實很簡單,我們只需要區分好他們的功能就行了。假如有 A、B、C 三個人去荒野求生,他們到了一個小島靠打獵爲生。A 負責打獵,B 負責加工,C 負責存儲。反應快的朋友可能知道我要說什麼了,這不就是代碼中的分層嗎。看看這樣命名是否合適:

看下我們代碼是不是類似的:

所以對主體起名字的關鍵在於定義他們的能力或者職責,然後使用擬人的方法起名。

對行爲命名

有了主體、客體,只要給行爲一個動詞,也就是我們的方法名,我們就可以像主謂賓一樣寫出句子了,是不是很簡單?

但是這個時候很多朋友就犯難了,我除了會 get、take、do 這類詞彙之外,找不到其它詞彙了。

實際上這是對業務理解不夠,或者英語詞彙量的限制。這類詞彙在英語中叫做小詞,往往威力無窮,但表達能力拉胯。這裏介紹一個學習英語的技巧,如果我們出國旅遊,其實也只需要 get、take、do、I、it 等幾個詞就夠了。如果想要買東西,就指着想要買的東西說,I take it,老闆自然就知道你的意思。然後不斷用更準備的詞去代替這些詞,然後英語就可以漸進提升。

英語的學習的關鍵不是背單詞,關鍵在於表達能力。但是不使用更準確的詞彙,表達能力就會受限。同理,我們可以使用 doXXX 來完成所有的業務,也能寫出整潔的代碼,但是表達能力非常弱。

所以對方法進行命名,只需要找一個合適的動詞即可。

那麼,動詞如果真的不夠用怎麼辦?

試想,如果有兩個方法,類名、方法名、參數都相同,那麼需要思考一個問題,這兩個方法的區別是什麼?這也是方法簽名爲什麼這樣定義的原因。

  1. 關於命名的反模式

下面通過一些命名的反模式,來對比主客體命名法的優點。

命名毫無意義

使用 a、b、c 進行命名,就像四川人使用 “大娃、二娃、幺娃子” 來命名一樣,只能算小名,沒人能看得懂。

還有使用拼音(甚至粵語拼音)、符號、不統一的風格,批評這類命名的文章已經很多了,不是本文的重點。

不遵守主客思維

下面幾個是需要重點介紹的反模式。

不遵守主客思維的命名有拿物品作爲主語,這類命名我稱爲 “成精” 命名法。比如我總喜歡用的例子,訂單中的結賬方法、商品中的發貨方法,可讀性非常差。

提示一下,由於主客體具有相對性,擬人的不一定不能作爲客體,就好比理髮師也能被其他理髮師理髮一樣。但是主體我們儘量使用擬人法,特殊情況是當一個對象操作它自己的屬性時候,我們能看做一個局部的主客關係,也能作爲主體。

過於抽象

在一個系統中,如果看到命名全是 xxxData、xxxMessage、xxxInfo 等非常通用和抽象的詞彙,基本沒有表達能力,造成混亂。

這是由於我們對客體認識不足造成的,按照前面對客體進行重新定義,這也是設計的一部分。

主體或者客體冗餘

在主客體命名法中,行爲只需要一個動詞,或者動詞短語即可,如果你的方法名形如:

當我們的方法被調用時,帶上參數,會看起來彆扭:

orderService.createUser(user)

如果能熟練的掌握主客體命名法,就能寫出這樣的代碼:

orderService.create(user)

如果主體、客體能表達完整的含義,行爲就是用一個動詞即可;如果不能,就使用一個動詞短語。

  1. 起名字可以反思設計

很多建模和架構問題甚至不需要費神去解決,找到一個恰如其分的名字可能就解決了。

命名是編程中非常讓人頭疼的事情,但是你可能不會相信,取一個好名字你的建模問題也解決了,這個問題說起來還真是挺有意思,否則也不值得寫一篇文章了。

在保險領域,業務有一個需求是在正式提交簽約後,保單才具有法律效應,正式生效。但是在受理簽約之前,用戶會提交一些材料,這些材料幾乎和最終的保單一模一樣。

最初的開發人員設計了 Policy 這個對象,並增加了一個狀態屬性,但狀態爲生效後保單才成爲合法的保單。這樣做看起來沒有問題,但是隨着業務的變化,簽約前和簽約後慢慢開始有了差異,仍然使用 Policy 這個對象不是很好。開發人員準備準備將這些差異分離,這個時候出現了兩個派別,併發生了爭吵。

主分派:簽約前後,這是兩個不同對象應該分離。

主合派:他們明明都是 Policy,怎麼能分了,再說分開了簽約前叫什麼呢?

主分派:…… 好像確實不知道叫什麼。

主合派:看吧,你都不知道叫什麼,還是別拆吧。

這類對話在我培訓或者諮詢工作中,聽到不下 10 次,如果有明確的命名來區分概念,往往大家很認同拆分,但是在不知道如何起名的時候,問題就變得模棱兩可。

所以說,命名的問題,本質是一個設計問題。

上面問題最終通過找到一個業界公認的詞彙得以解決——投保單,英文中叫做 insurance slip。類似的概念還有客戶、用戶、賬戶的三戶設計,當我們找到了命名後,建模問題往往迎刃而解。

小的時候幾乎每家都有一本書《姓名與人生》,用來給新生兒起名字。它提供了一套根據筆畫來判斷名字是否足夠好的理論,雖然現在看來有點扯,但是也意味着人們對名字的重視情況。

優秀的開發者對待命名應該像對待自己孩子的名字一樣,畢竟他們有一個共同點就是,被廣泛使用後基本上很難被修改。

在開源社區,優秀的圖形庫 mxGraph 爲了命名這事兒討論了好幾年,直到 2021 纔開始下決心修改它。

“反者道之動”,反過來想也能幫我們更好的發現問題。

當我們無法給出一個良好的命名的時候,反過來思考,當我們無法給出一個良好的命名的時候,是不是意味着我們的軟件設計需要改善呢?

  1. 總結

  1. 給行爲的主體命名,使用擬人法,想象在給人起名字。比如 Manager、Handler、Controller。

  2. 給行爲的客體命名,使用擬物法,想象在給物品起名字。

  3. 對行爲起一個符合主體、客體 ” 身份 “的動詞,比如 handle、save、process 等。

  4. 嘗試連成一個句子,避免冗餘和重複。主體 + 行爲 + 客體 = 主謂賓結構。

  5. 使用具象化的命名,不使用抽象的名字。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/YyYxaCKhJOLEB6fyBjyQpg