领域驱动设计(DDD)前夜:面向对象思想
面向對象
面向對象是一種對世界理解和抽象的方法。那么對象是什么呢?
對象是對世界的理解和抽象,世界又代稱為萬物。理解世界是比較復雜的,但是世界又是由事物組成的。
正是這樣的一種關系,認識事物是極其重要的。那什么是事物呢?
事物:由事和物兩個方面組成。事即事情,物即物體,那什么是事情?什么是物體呢?
意志的行為是為事。
存在的一切是為物,物體又是由屬性和行為組成的。
由于對象是對事物的理解和抽象,所以對象就是對一個事物的屬性和行為的理解和抽象。正是這樣的一種關系,面向對象就是對一個事物的屬性和行為的理解和抽象的方法。
理解對象以及抽象“對象”就是在理解和抽象事物的屬性和行為。
屬性和操作
面向對象的核心是對象,對象是由屬性和方法組合而成的。在使用面向對象進行分析、設計、編碼的時候,你首先應該想到的是屬性和方法組合形成的對象。在需要組合的時候就不應該出現只包含屬性的對象或者只包含方法的對象。
何時需要屬性和方法組合的對象呢?
何時只需要包含屬性的對象呢?
何時只需要包含方法的對象呢?
事物由事情和物體組成。事情是行為,物體是屬性。
當你需要抽象一個事物的事情和物體時就需要屬性和方法的組合。
當你只需要抽象一個事物的物體時就只需要屬性。
當你只需要抽象一個事物的事情時就只需要方法。
對象建模
在數據庫系統中,它們關心的是事物中的物體,所以在抽象事物時它們只抽象了事物中的屬性。在應用系統中,它們關心的是表達事物的三種方式(屬性和方法的組合、只包含屬性、只包含方法),所以在抽象事物時需要思考你需要那種方式。
只要需要抽象事物(事情和物體)中的屬性,也就是物體的這部分,那有可能是需要持久化的。只要需要持久化,通常是保存到關系型數據庫中,在關系型數據庫中的表(Table)基本上是與面向對象中的對象(Object)的屬性是一一對應的。
由于數據庫中的表只抽象了事物中的屬性,所以它有可能是不完整的。就抽象事物的屬性來說依然有兩種:只抽象事物的屬性、抽象事物的屬性和方法的組合。
正是數據庫中表的這種抽象形成了數據模型,它對比對象模型是不完整,所以在面向對象分析(OOA)時一定要采用對象(事物)抽象而不是數據(屬性、物體)抽象。
舉個例子:
簡單金融賬戶(Account)
屬性有:賬號(id)、余額(balance)、狀態(status)
操作有:開戶(open)、注銷(close)、存錢(credit)、取錢(debit)。
數據模型的只需要設計字段(fields)和關聯關系,所以下面的 SQL 基本已完成。
create table account (id integer,balance integer,status integer ); 復制代碼如果把上述 SQL 轉換成 Java 的對象的話,得到將是一個用面向對象設計的數據模型,而不是完整的對象模型。這種模型在 Java 開發中非常普遍,這是數據模型思維所導致的結果。
@Getter @Setter public class Account {private int id;private int balance;private AccountStatus status; } 復制代碼如果使用對象模型的思維來設計模型,從接口上來看,他應該是這樣的:
public interface Account {int getId();int getBalance();AccountStatus getStatus();void open();void close();void credit(int amount);void debit(int amount); } 復制代碼如果 Account 接口符合金融賬戶的設計,那么 Account 最簡單地實現應該如下:
@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;} }這是從兩個建模的角度來對比對象模型和數據模型的不同,下面我們還要從完整地執行流程來對比。
Account Credit
首先是使用數據模型所設計的時序圖,因為數據模型下的 Account 不包含業務邏輯,所有的業務邏輯都在 AccountService 中,所以通常稱為業務邏輯服務(層)或者事務腳本。如圖下:
使用 Java 代碼的實現:
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);} } 復制代碼現在我們要使用對象模型的思維進行設計時序圖
使用 Java 代碼的實現:
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 方法中已經沒有了業務代碼,更多地是協調調用執行流程。對于這種只用來實現執行流程,不在包含業務邏輯的服務對象,將它們稱為應用服務(Application Service)。
舉個家政服務公司與 AccountService 相似的例子:
比如你想請一位保潔阿姨給家里做一做清潔工作,首先是你打電話給家政服務公司說你要給家里做一做清潔工作,然后家政公司安排一位保潔阿姨去你家幫忙完成清潔工作,在這個過程中家政公司主要做了接待、協調、安排、最后可能包含一些保潔阿姨的績效等一系列工作。上面的 AccountService 也一樣是在做這樣的一件事情。所以在對象模型中,AccountService 只需要做像家政公司這樣協調工作,具體地工作由保潔阿姨來完成,這里的保潔阿姨就相當于 Account 對象。
從兩處對比來看,采用數據模型建模配合業務邏輯服務的方式更像是過程化編程,只是在使用面向對象語言來編寫過程化代碼。而采用對象模型配合應用服務的方式才是符合面向對象編程。
組合與聚合
在多數的業務開發中,普遍提到的是關聯關系(一對一、一對多、多對多)和繼承泛化,很少去關注組合與聚合,但是組合與聚合在面向對象中是相當重要的。
組合與聚合是在探討整體與部分的關系,這種整體與部分的關系是一種比關聯關系更強的關系。比如:汽車與輪胎,汽車是一個整體,輪胎是汽車的一部分。如果汽車沒有輪胎,那么就無法構成汽車的完整性,所以在討論整體與部分的關系時,要特別注意整體對部分的依賴性而不是部分對整體的依賴。
首先通過一個人進食過程的用例來考慮整體與部分的依賴關系,然后在例子中說明組合與聚合區別和聯系。
這個進食過程需要多個人體器官協作配合。首先是通過一種方式將食物送進口腔,由牙齒的咀嚼和舌頭的攪拌,再由喉嚨吞咽,從食道進入胃中,在通過胃里進行初步消化,將飲食變成食糜,然后傳入小腸后,在脾的運化作用下,精微物質被吸收。
注意:這個從嘴到胃的執行過程并不是一個 Input/Output 方式,而是一個 Stream 方式,后面還有連接。從這個角度來考慮嘴只是 Stream 的入口,但是這個用例主要是想說明整體與部分的聯系,所以把這種連接的每一個部分修改成 Input/Output 調用方式。
為這次進食過程來建模吧!首先確定關鍵的對象模型有:人(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); // 在腸道里吸收營養。// 便秘中...} }public class PersonTests {public static void main(String[] args) {new Person().eat("chips");} }在整個進食流程中,是由人(Person)做的吃(eat)這個動作開始,然后由人體內部的多個參與的部分對象協調完成的,這就是整體與部分的關系。Person 是個整體,Mouth, Esophagus, Stomach, Intestine 是整體內的部分。然后在考慮一個事情,這些部分對象是不是依附在整體對象上,比如:嘴是不是獨立于人體不能存活,伴隨著人的存在而存在,消亡而消亡。這種部分對象的創建、存在和消亡都是和整體對象一起的就稱為組合。而聚合就不像組合的整體與部分的關系那么強,比如:汽車與輪胎是一個整體與部分的關系,汽車沒有輪胎肯定跑不了。但是汽車可以更換輪胎,這種可以更換的關系就沒有組合關系那么強。除了更換還有缺少的,比如:螃蟹有八條腿,總的來說螃蟹沒有腿肯定是無法行走的,但是缺少一個兩個還是能行走的,可能行走有一些困難。這樣的可以在初始化之后能夠更換的或者不需要強制完整的整體與部分的關系稱之為聚合。
隨著時間的向前和空間的擴大,組合和聚合還是會存在轉換的情況,比如未來人可以換嘴、進食流程不需要嘴的參與,再比如說一次性轎車,出廠后就不能維修更換等等。所以在討論組合與聚合的關系時,要在一定的限界下來討論。
開源電商
Mallfoundry 是一個完全開源的使用 Spring Boot 開發的多商戶電商平臺。它可以嵌入到已有的 Java 程序中,或者作為服務器、集群、云中的服務運行。
領域模型采用領域驅動設計(DDD)、接口化以及面向對象設計。
項目地址:gitee.com/mallfoundry…
總結
對象建模,通過對象模型與數據模型的對比來說明需要一種對象模型的思維。
對象建模的應用,通過賬戶存款的業務來簡要說明如何使用對象模型。
組合與聚合,通過重點說明組合與聚合,讓其在對象模型的基礎上,討論整體與部分的關系。
本文轉自網友:不夠具體?發布的領域驅動設計系列文章,點擊閱讀原文可以訪問作者主頁
https://juejin.cn/user/4441682708804142
·END·
領域驅動文章推薦
領域驅動設計(DDD):領域和子域
美團技術總結:Java中9種常見的CMS GC問題分析與解決
歐創新:深度解析DDD中臺和微服務設計
領域驅動專家張逸文字脫口秀:簡單工廠不簡單
DDD專家張逸:《解構領域驅動設計》前言
Hacker News熱文:請停止學習框架,學習領域驅動設計(DDD)(獲500個點贊)
京東平臺研發朱志國:領域驅動設計(DDD)理論啟示
DDD專家張逸:構建領域驅動設計知識體系
領域驅動設計(DDD)在美團點評業務系統的實踐
當DDD遇上微服務
DDD戰略篇:架構設計的響應力
可視化與領域驅動設計
? ?END ? ?? #技術人必備#點個在看,讓更多人看見
新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!總結
以上是生活随笔為你收集整理的领域驱动设计(DDD)前夜:面向对象思想的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nyoj-228(士兵杀敌五) hdu-
- 下一篇: nyoj- 117 求逆序数 hdu-