<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聽說過「DCI」嗎?

          共 6175字,需瀏覽 13分鐘

           ·

          2023-01-03 00:52

          這里是Z哥的個人公眾號

          每周五11:45 按時送達

          當然了,也會時不時加個餐~

          我的第「229」篇原創(chuàng)敬上



          大家好呀,好久不見,我是Z哥。在我最近消失的這段時間里,有些小伙伴微信問我去哪了,老讀者應該知道哈,Z哥自從換了工作后的確太忙,打理公眾號的時間都用來加班了。一晃眼,不知不覺都過去4個月了,太慚愧了。 不過最近收獲還挺多的,我接下來慢慢花時間整理出來分享給大家。
          前幾周和團隊里的「DDD」愛好者交流的時候,有人提到了一個概念叫「DCI」。我當時就想我只知道「DI」和「CI」,「DCI」又是什么鬼。后來在網(wǎng)上搜了一下才發(fā)現(xiàn)在 2009 年這個概念就被提出來了,當時的文章是《The DCI Architecture: A New Vision of Object-Oriented Programming》。
          DCI 中的 3 個字母分別代表:Data,Context,Interactive。在我看來,這 3 個概念共同配合表達出了某個“角色”做的事情:
          「誰/什么東西(Data)」-「在什么場景下(Context)」-「做什么事(Interactive)」
          比如,當你在家里打掃衛(wèi)生的時候,你的角色其實是“清潔工”;當你在家里燒飯的時候,你的角色是“廚師”;當你在家里運動的時候,你的角色是“運動者”。你看,同樣的一個人在不同場景下所產(chǎn)生的行為都與當時所處的角色有關(guān)。
          DCI 的核心就是那個 Interactive,也是“角色”這個概念存在的地方。


          我們來舉個例子看看它的作用。 就拿前面提到的例子來看,如果我們只是識別出其中的實體是 Person,那么自然打掃衛(wèi)生的方法 Clean(),以及燒飯的方法 Cook(),和運動的方法 Exercise()自然而然就落到了 Person 這個實體上。那么問題就來了,一個人在社會生活中需要做的事情有很多,如果按照這個思路,Person 這個實體將成為一個上帝類。
                
                  func?(p?Person)?Clean()?{
                
                
                    fmt.Println("Clean")
                
                
                  }
                
                
                  
                    
          func (p Person) CookFood() { fmt.Println("CookFood") }
          func (p Person) Sport() { fmt.Println("Sport") }
          type Person struct { Name string Age int }


          同時,如果有某個領(lǐng)域服務或者實體上的方法以 Person 作為參數(shù),那么其內(nèi)部將可以任意調(diào)用 Clean()、Cook() 或者 Exercise()。
                
                  func MethodA(p Person){
                
                
                    p.Clean()
                
                
                    p.CookFood()
                
                
                    p.Sport()
                
                
                  }
                
              
          這很明顯與 OO 思想中的核心概念「高內(nèi)聚低耦合」背道而馳,違反了「迪米特法則」。
          而函數(shù)式編程的三層架構(gòu)之所以流行了很多年,就是因為它的世界里主要關(guān)注的是顆粒度最小的「方法」應該放到三層中的哪一層,而對「方法」在某一層內(nèi)放到哪個對象之中是沒有明確規(guī)定的。 因此在上面的例子中,如果我們將Clean()、Cook() 和 Exercise() 分別寫在不同的 XXXService 中,并同時接收 Person 作為入?yún)?,那么可以輕松地消除“上帝類”,但與此同時 Person 也成為了一個只有屬性的「貧血模型」。 因此 DCI 的提出就是通過一個新的視角來定義對象,它通過增加一層概念——「角色」,將傳統(tǒng) DDD 中對象上的非通用方法轉(zhuǎn)移到了不同的角色對象中,避免了需要在一個對象上同時表達“是什么”和“能做什么”而可能出現(xiàn)的「上帝類」問題。 同時,通過由多個角色組成的對象也避免了「貧血模型」的發(fā)生。
          接下來看看如何使用 DCI 來重新設計上面的代碼。其實很簡單,將 Person 設計成由 3 個角色 Cleaner、Cook、Sporter 組成,在每個角色中分別定義 Clean()、Cook() 和 Exercise()方法。
                
                  type Cleaner interface {
                
                
                    Clean()
                
                
                  }
                
                
                  
                    
          type Cook interface { CookFood() }
          type Sporter interface { Sport() }
          type Person struct { Name string Age int }
          func (p Person) Clean() { fmt.Println("Clean") }
          func (p Person) CookFood() { fmt.Println("CookFood") }
          func (p Person) Sport() { fmt.Println("Sport") }
          func DoSomeThing(cook Cook) { fmt.Println("DoSomeThing") cook.CookFood() }
          func main() { ??var?p?Person p.Clean() p.CookFood() p.Sport() DoSomeThing(p) }
          如此一來,我們可以將一些使用 Person 作為入?yún)⒌姆椒ㄕ{(diào)整成相應的角色,以達到「迪米特法則」所提倡的效果。

          我們再想深入一步,Cook() 和 Clean() 的實現(xiàn)中都需要“拿起東西”,這是一個和角色無關(guān)的行為,那么可以將它直接定義在Person中。
                
                  func (p Person) TakeUp(thing string) {
                
                
                    fmt.Println(fmt.Sprintf("%s TakeUp a %s", p.Name, thing))
                
                
                  }
                
                
                  
                    
          func (p Person) Clean() { p.TakeUp("掃帚") fmt.Println("Clean") }
          func (p Person) CookFood() { p.TakeUp("鍋子") fmt.Println("CookFood") }
          在 DCI 中,將角色上定義的方法稱作「Role Method」,將對象(Data)上定義的方法稱作「Local Method」。 前者是填充業(yè)務邏輯的地方,而后者更像是對象(Data)自身天然具有的能力,與業(yè)務邏輯無關(guān)。
          上面的這整套實現(xiàn)邏輯在 DCI 中被稱作 Methodless Role,與之對應的還有 Methodful Role 的實現(xiàn)邏輯,在這里就不展開了。顧名思義就是在角色的定義上更豐富,將「Local Method」也定義出來。另外,增加一層「角色」的概念后,我們可以發(fā)現(xiàn),任何具有相同行為的對象都可以給他設置同一個角色。比如,機器人也可以打掃,那么這個 Cleaner 的角色也可以定義到 Robot 對象中,而不僅僅是 Person 對象。只不過,Robot.Clean() 的實現(xiàn)不是“拿起掃帚”,而是“制定一個行走路線”,然后它自己會把垃圾吸到自己身體里。
          可能你會問,Context 呢?好像一直沒提到它該怎么實現(xiàn)?以 Z 哥目前的理解來看,Context 所做的事情其實和傳統(tǒng) DDD 中的 Applicaion 層做的事情是重合的,只是代碼結(jié)構(gòu)的不同。因此,我認為這部分倒不是重點,你可以按照原先的 Application 層代碼來寫,相當于每一個 Application 層中的方法就是一個 Context。
          本質(zhì)上說,DCI 是一種 “角色接口” 設計思想,如果習慣 OO 編程的小伙伴應該是很熟悉這種寫法的。
          好了,我們總結(jié)一下。這篇呢,Z哥和你分享了我對 DCI 的了解。它通過引入「角色」的概念,將傳統(tǒng) DDD 建模時賦予「對象」的兩個職責“是什么”和“能做什么”中的后者拆分到「角色」中去定義,避免上帝類問題。同時,因為角色最終還是會作用到「對象」上,所以也不會出現(xiàn)函數(shù)式編程中的貧血模型問題。DCI 中,對定義在「角色」上的方法稱為 Role Method,而直接定義在「對象」上的方法稱作 Local Method。對于「角色」在編碼的實現(xiàn),一般建議使用 interface 的方式來體現(xiàn),因為“角色只定義行為”,具體行為要怎么做,由所在的對象來實現(xiàn)。如此符合「依賴倒置原則」的場景自然適合用 interface 來實現(xiàn)。
          最后,Z 哥再分享一個實踐 DCI 的思路給你。首先是什么時候需要用 DCI?當你在實踐 DDD 的過程中,覺得某個對象過大了,有點上帝類的味道,這時候就可以想一下是否可以通過 DCI 來重新設計一下。如何落地DCI?分為以下四步:
          1. 識別領(lǐng)域場景

          2. 羅列其中的業(yè)務行為

          3. 分析這些定位屬于什么角色,定義角色接口

          4. 確定承擔這些角色的數(shù)據(jù)對象,定義數(shù)據(jù)類以及數(shù)據(jù)類的本地方法


          好了,今天就聊這些,希望對你有所啟發(fā)。


          推薦閱讀:


          原創(chuàng)不易,如果你覺得這篇文章還不錯,就「 點贊 」或者「在看」一下吧,鼓勵我的創(chuàng)作 :)


          也可以分享我的公眾號名片給有需要的朋友們。


          如果你有關(guān)于軟件架構(gòu)、分布式系統(tǒng)、產(chǎn)品、運營的困惑

          可以試試點擊「閱讀原文

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  毛片一级免费 | 日本中文字幕中出在线 | 蜜臀无码在线 | 天堂俺去俺来也WWW | 国产成人在线观看免费网站 |