日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

领域驱动设计(DDD)前夜:面向对象思想

發(fā)布時間:2025/3/16 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 领域驱动设计(DDD)前夜:面向对象思想 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

面向?qū)ο?br />

面向?qū)ο笫且环N對世界理解和抽象的方法。那么對象是什么呢?

對象是對世界的理解和抽象,世界又代稱為萬物。理解世界是比較復雜的,但是世界又是由事物組成的。

正是這樣的一種關(guān)系,認識事物是極其重要的。那什么是事物呢?

事物:由事和物兩個方面組成。事即事情,物即物體,那什么是事情?什么是物體呢?

  • 意志的行為是為事。

  • 存在的一切是為物,物體又是由屬性和行為組成的。

由于對象是對事物的理解和抽象,所以對象就是對一個事物的屬性和行為的理解和抽象。正是這樣的一種關(guān)系,面向?qū)ο缶褪菍σ粋€事物的屬性和行為的理解和抽象的方法。

理解對象以及抽象“對象”就是在理解和抽象事物的屬性和行為。

屬性和操作

面向?qū)ο蟮暮诵氖菍ο?#xff0c;對象是由屬性和方法組合而成的。在使用面向?qū)ο筮M行分析、設(shè)計、編碼的時候,你首先應該想到的是屬性和方法組合形成的對象。在需要組合的時候就不應該出現(xiàn)只包含屬性的對象或者只包含方法的對象。

  • 何時需要屬性和方法組合的對象呢?

  • 何時只需要包含屬性的對象呢?

  • 何時只需要包含方法的對象呢?

事物由事情和物體組成。事情是行為,物體是屬性。

  • 當你需要抽象一個事物的事情和物體時就需要屬性和方法的組合。

  • 當你只需要抽象一個事物的物體時就只需要屬性。

  • 當你只需要抽象一個事物的事情時就只需要方法。

對象建模

在數(shù)據(jù)庫系統(tǒng)中,它們關(guān)心的是事物中的物體,所以在抽象事物時它們只抽象了事物中的屬性。在應用系統(tǒng)中,它們關(guān)心的是表達事物的三種方式(屬性和方法的組合、只包含屬性、只包含方法),所以在抽象事物時需要思考你需要那種方式。

只要需要抽象事物(事情和物體)中的屬性,也就是物體的這部分,那有可能是需要持久化的。只要需要持久化,通常是保存到關(guān)系型數(shù)據(jù)庫中,在關(guān)系型數(shù)據(jù)庫中的表(Table)基本上是與面向?qū)ο笾械膶ο?#xff08;Object)的屬性是一一對應的。

由于數(shù)據(jù)庫中的表只抽象了事物中的屬性,所以它有可能是不完整的。就抽象事物的屬性來說依然有兩種:只抽象事物的屬性、抽象事物的屬性和方法的組合。

正是數(shù)據(jù)庫中表的這種抽象形成了數(shù)據(jù)模型,它對比對象模型是不完整,所以在面向?qū)ο蠓治?#xff08;OOA)時一定要采用對象(事物)抽象而不是數(shù)據(jù)(屬性、物體)抽象。

舉個例子:

簡單金融賬戶(Account)

屬性有:賬號(id)、余額(balance)、狀態(tài)(status)

操作有:開戶(open)、注銷(close)、存錢(credit)、取錢(debit)。

數(shù)據(jù)模型的只需要設(shè)計字段(fields)和關(guān)聯(lián)關(guān)系,所以下面的 SQL 基本已完成。

create table account (id integer,balance integer,status integer ); 復制代碼

如果把上述 SQL 轉(zhuǎn)換成 Java 的對象的話,得到將是一個用面向?qū)ο笤O(shè)計的數(shù)據(jù)模型,而不是完整的對象模型。這種模型在 Java 開發(fā)中非常普遍,這是數(shù)據(jù)模型思維所導致的結(jié)果。

@Getter @Setter public class Account {private int id;private int balance;private AccountStatus status; } 復制代碼

如果使用對象模型的思維來設(shè)計模型,從接口上來看,他應該是這樣的:

public interface Account {int getId();int getBalance();AccountStatus getStatus();void open();void close();void credit(int amount);void debit(int amount); } 復制代碼

如果 Account 接口符合金融賬戶的設(shè)計,那么 Account 最簡單地實現(xiàn)應該如下:

@Getter public class Account {private int id;private int balance;private AccountStatus status;public void open() {this.status = AccountStatus.OPENED;}public void close() {this.status = AccountStatus.CLOSED;}public void credit(int amount) {this.balance += amount;}public void debit(int amount) {this.balance -= amount;} }

這是從兩個建模的角度來對比對象模型和數(shù)據(jù)模型的不同,下面我們還要從完整地執(zhí)行流程來對比。

Account Credit

首先是使用數(shù)據(jù)模型所設(shè)計的時序圖,因為數(shù)據(jù)模型下的 Account 不包含業(yè)務(wù)邏輯,所有的業(yè)務(wù)邏輯都在 AccountService 中,所以通常稱為業(yè)務(wù)邏輯服務(wù)(層)或者事務(wù)腳本。如圖下:

使用 Java 代碼的實現(xiàn):

public class AccountService {private final AccountRepository accountRepository;public AccountService(AccountRepository accountRepository) {this.accountRepository = accountRepository;}public Account creditAccount(int accountId, int amount) {var account = this.accountRepository.findById(accountId).orElseThrow(() -> new AccountException("The Account was not found"));if (AccountStatus.OPENED != account.getStatus()) {throw new AccountException("The Account is not open");}account.setBalance(account.getBalance() + amount);return this.accountRepository.save(account);} } 復制代碼

現(xiàn)在我們要使用對象模型的思維進行設(shè)計時序圖

使用 Java 代碼的實現(xiàn):

public class AccountService {private final AccountRepository accountRepository;public AccountService(AccountRepository accountRepository) {this.accountRepository = accountRepository;}public Account creditAccount(int accountId, int amount) {var account = this.accountRepository.findById(accountId).orElseThrow(() -> new AccountException("The Account was not found"));account.debit(amount);return this.accountRepository.save(account);} }

在 AccountService 的 creditAccount 方法中已經(jīng)沒有了業(yè)務(wù)代碼,更多地是協(xié)調(diào)調(diào)用執(zhí)行流程。對于這種只用來實現(xiàn)執(zhí)行流程,不在包含業(yè)務(wù)邏輯的服務(wù)對象,將它們稱為應用服務(wù)(Application Service)。

舉個家政服務(wù)公司與 AccountService 相似的例子:

比如你想請一位保潔阿姨給家里做一做清潔工作,首先是你打電話給家政服務(wù)公司說你要給家里做一做清潔工作,然后家政公司安排一位保潔阿姨去你家?guī)兔ν瓿汕鍧嵐ぷ?#xff0c;在這個過程中家政公司主要做了接待、協(xié)調(diào)、安排、最后可能包含一些保潔阿姨的績效等一系列工作。上面的 AccountService 也一樣是在做這樣的一件事情。所以在對象模型中,AccountService 只需要做像家政公司這樣協(xié)調(diào)工作,具體地工作由保潔阿姨來完成,這里的保潔阿姨就相當于 Account 對象。

從兩處對比來看,采用數(shù)據(jù)模型建模配合業(yè)務(wù)邏輯服務(wù)的方式更像是過程化編程,只是在使用面向?qū)ο笳Z言來編寫過程化代碼。而采用對象模型配合應用服務(wù)的方式才是符合面向?qū)ο缶幊獭?/p>

組合與聚合

在多數(shù)的業(yè)務(wù)開發(fā)中,普遍提到的是關(guān)聯(lián)關(guān)系(一對一、一對多、多對多)和繼承泛化,很少去關(guān)注組合與聚合,但是組合與聚合在面向?qū)ο笾惺窍喈斨匾摹?/p>

組合與聚合是在探討整體與部分的關(guān)系,這種整體與部分的關(guān)系是一種比關(guān)聯(lián)關(guān)系更強的關(guān)系。比如:汽車與輪胎,汽車是一個整體,輪胎是汽車的一部分。如果汽車沒有輪胎,那么就無法構(gòu)成汽車的完整性,所以在討論整體與部分的關(guān)系時,要特別注意整體對部分的依賴性而不是部分對整體的依賴。

首先通過一個人進食過程的用例來考慮整體與部分的依賴關(guān)系,然后在例子中說明組合與聚合區(qū)別和聯(lián)系。

這個進食過程需要多個人體器官協(xié)作配合。首先是通過一種方式將食物送進口腔,由牙齒的咀嚼和舌頭的攪拌,再由喉嚨吞咽,從食道進入胃中,在通過胃里進行初步消化,將飲食變成食糜,然后傳入小腸后,在脾的運化作用下,精微物質(zhì)被吸收。

注意:這個從嘴到胃的執(zhí)行過程并不是一個 Input/Output 方式,而是一個 Stream 方式,后面還有連接。從這個角度來考慮嘴只是 Stream 的入口,但是這個用例主要是想說明整體與部分的聯(lián)系,所以把這種連接的每一個部分修改成 Input/Output 調(diào)用方式。

為這次進食過程來建模吧!首先確定關(guān)鍵的對象模型有:人(Person)、嘴(Mouth)、食道(Esophagus)、胃(Stomach)、腸道(Intestine)。代碼如下:

// 嘴 public class Mouth {public Object chew(Object food) {return food;} }// 食道 public class Esophagus {public Object transfer(Object paste) {return paste;} }// 胃 public class Stomach {public Object fill(Object paste) {return paste;} }// 腸道 public class Intestine {public void absorb(Object chyme) {// absorbing...} }public class Person {private final Mouth mouth;private final Esophagus esophagus;private final Stomach stomach;private final Intestine intestine;public Person() {this.mouth = new Mouth();this.esophagus = new Esophagus();this.stomach = new Stomach();this.intestine = new Intestine();}public void eat(Object food) { // 進食。var paste = this.mouth.chew(food); // 咀嚼形成漿糊。paste = this.esophagus.transfer(paste); // 通過食道傳送食物。var chyme = this.stomach.fill(paste); // 填充到胃里形成食糜。this.intestine.absorb(chyme); // 在腸道里吸收營養(yǎng)。// 便秘中...} }public class PersonTests {public static void main(String[] args) {new Person().eat("chips");} }

在整個進食流程中,是由人(Person)做的吃(eat)這個動作開始,然后由人體內(nèi)部的多個參與的部分對象協(xié)調(diào)完成的,這就是整體與部分的關(guān)系。Person 是個整體,Mouth, Esophagus, Stomach, Intestine 是整體內(nèi)的部分。然后在考慮一個事情,這些部分對象是不是依附在整體對象上,比如:嘴是不是獨立于人體不能存活,伴隨著人的存在而存在,消亡而消亡。這種部分對象的創(chuàng)建、存在和消亡都是和整體對象一起的就稱為組合。而聚合就不像組合的整體與部分的關(guān)系那么強,比如:汽車與輪胎是一個整體與部分的關(guān)系,汽車沒有輪胎肯定跑不了。但是汽車可以更換輪胎,這種可以更換的關(guān)系就沒有組合關(guān)系那么強。除了更換還有缺少的,比如:螃蟹有八條腿,總的來說螃蟹沒有腿肯定是無法行走的,但是缺少一個兩個還是能行走的,可能行走有一些困難。這樣的可以在初始化之后能夠更換的或者不需要強制完整的整體與部分的關(guān)系稱之為聚合。

隨著時間的向前和空間的擴大,組合和聚合還是會存在轉(zhuǎn)換的情況,比如未來人可以換嘴、進食流程不需要嘴的參與,再比如說一次性轎車,出廠后就不能維修更換等等。所以在討論組合與聚合的關(guān)系時,要在一定的限界下來討論。

開源電商

Mallfoundry 是一個完全開源的使用 Spring Boot 開發(fā)的多商戶電商平臺。它可以嵌入到已有的 Java 程序中,或者作為服務(wù)器、集群、云中的服務(wù)運行。

  • 領(lǐng)域模型采用領(lǐng)域驅(qū)動設(shè)計(DDD)、接口化以及面向?qū)ο笤O(shè)計。

項目地址:gitee.com/mallfoundry…

總結(jié)

  • 對象建模,通過對象模型與數(shù)據(jù)模型的對比來說明需要一種對象模型的思維。

  • 對象建模的應用,通過賬戶存款的業(yè)務(wù)來簡要說明如何使用對象模型。

  • 組合與聚合,通過重點說明組合與聚合,讓其在對象模型的基礎(chǔ)上,討論整體與部分的關(guān)系。

本文轉(zhuǎn)自網(wǎng)友:不夠具體?發(fā)布的領(lǐng)域驅(qū)動設(shè)計系列文章,點擊閱讀原文可以訪問作者主頁

https://juejin.cn/user/4441682708804142

·END·

領(lǐng)域驅(qū)動文章推薦

領(lǐng)域驅(qū)動設(shè)計(DDD):領(lǐng)域和子域

美團技術(shù)總結(jié):Java中9種常見的CMS GC問題分析與解決

歐創(chuàng)新:深度解析DDD中臺和微服務(wù)設(shè)計

領(lǐng)域驅(qū)動專家張逸文字脫口秀:簡單工廠不簡單

DDD專家張逸:《解構(gòu)領(lǐng)域驅(qū)動設(shè)計》前言

Hacker News熱文:請停止學習框架,學習領(lǐng)域驅(qū)動設(shè)計(DDD)(獲500個點贊)

京東平臺研發(fā)朱志國:領(lǐng)域驅(qū)動設(shè)計(DDD)理論啟示

DDD專家張逸:構(gòu)建領(lǐng)域驅(qū)動設(shè)計知識體系

領(lǐng)域驅(qū)動設(shè)計(DDD)在美團點評業(yè)務(wù)系統(tǒng)的實踐

當DDD遇上微服務(wù)

DDD戰(zhàn)略篇:架構(gòu)設(shè)計的響應力

可視化與領(lǐng)域驅(qū)動設(shè)計

? ?END ? ?? #技術(shù)人必備#

點個在看,讓更多人看見

新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!

總結(jié)

以上是生活随笔為你收集整理的领域驱动设计(DDD)前夜:面向对象思想的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。