打造堅如磐石的代碼:18種高內(nèi)聚原則精解
高內(nèi)聚原則是GRASP(General Responsibility Assignment Software Principles)中的一個重要原則,它強調(diào)一個類或模塊應(yīng)該只包含與其功能緊密相關(guān)的操作和數(shù)據(jù),從而提高代碼的可讀性和可維護性。
1. 高內(nèi)聚設(shè)計圖
高內(nèi)聚原則鼓勵開發(fā)者將相關(guān)的數(shù)據(jù)和處理數(shù)據(jù)的方法封裝在同一個類中,一個類或模塊只負責一項任務(wù),使得每個類都成為其數(shù)據(jù)的“專家”。
在這里插入圖片描述
Order
:代表訂單,包含訂單編號、商品項列表、用戶、支付方式等屬性,以及添加商品項、處理支付等方法。OrderItem
:代表訂單中的商品項,包含產(chǎn)品、數(shù)量和價格屬性,以及計算總價的方法。Customer
:代表用戶,包含用戶編號、名字、電子郵件等屬性,以及獲取全名的方法。PaymentMethod
:代表支付方式,包含支付詳情屬性,以及驗證支付方法和處理支付的方法。Product
:代表產(chǎn)品,包含產(chǎn)品編號、名稱和價格屬性。OrderStatus
:一個枚舉,定義了訂單可能的狀態(tài)。
1. 高內(nèi)聚解決的問題
此原則解決了數(shù)據(jù)和行為分離的問題,避免了在系統(tǒng)中不必要的數(shù)據(jù)傳遞和復雜性。
3. 高內(nèi)聚特點
- 數(shù)據(jù)封裝:將數(shù)據(jù)和處理數(shù)據(jù)的方法封裝在同一個類中。
- 高內(nèi)聚性:類具有高內(nèi)聚性,每個類都專注于處理自己的數(shù)據(jù)。
- 低耦合度:類之間的耦合度降低,因為每個類都管理自己的數(shù)據(jù)。
4. 高內(nèi)聚缺點
- 過度封裝:在某些情況下,可能會導致過度封裝,使得類過于復雜。
- 職責不明確:在一些復雜的系統(tǒng)中,職責的劃分可能不夠明確。
5. 高內(nèi)聚使用場景
當系統(tǒng)中的類需要處理特定數(shù)據(jù)時,應(yīng)考慮應(yīng)用高內(nèi)聚原則。
6. 高內(nèi)聚案例
6.1 訂單管理系統(tǒng)案例
訂單管理系統(tǒng)處理訂單數(shù)據(jù)和相關(guān)業(yè)務(wù)邏輯。
實現(xiàn)前(違反高內(nèi)聚原則):
1publicclassOrder{
2privateString orderId;
3privatedouble totalAmount;
4privateList<OrderItem> items;
5privateCustomer customer;
6privatePaymentMethod paymentMethod;
7
8// 僅包含基本的getter和setter方法
9}
10
11publicclassOrderService{
12privateList<Order> orders;
13
14publicvoidprocessOrderPayment(Order order){
15// 處理訂單支付邏輯
16// 可能包括驗證支付方法、計算總金額等
17}
18}
實現(xiàn)后(遵循高內(nèi)聚原則):
1publicclassOrder{
2privateString orderId;
3privateList<OrderItem> items;
4privateCustomer customer;
5privatePaymentMethod paymentMethod;
6privateOrderStatus status;
7
8publicOrder(String orderId,Customer customer,PaymentMethod paymentMethod){
9this.orderId = orderId;
10this.customer = customer;
11this.paymentMethod = paymentMethod;
12this.status =OrderStatus.PENDING;
13this.items =newArrayList<>();
14}
15
16publicvoidaddItem(OrderItem item){
17 items.add(item);
18updateTotalAmount();
19}
20
21privatevoidupdateTotalAmount(){
22double total = items.stream().mapToDouble(OrderItem::getTotalPrice).sum();
23// 更新訂單總金額邏輯
24}
25
26publicvoidprocessPayment(){
27if(paymentMethod.isValid()){
28 paymentMethod.charge(getTotalAmount());
29 status =OrderStatus.COMPLETED;
30}else{
31 status =OrderStatus.FAILED;
32}
33}
34
35publicdoublegetTotalAmount(){
36return items.stream().mapToDouble(OrderItem::getTotalPrice).sum();
37}
38
39publicOrderStatusgetStatus(){
40return status;
41}
42
43// 其他與訂單相關(guān)的業(yè)務(wù)邏輯
44}
45
46publicclassOrderItem{
47privateProduct product;
48privateint quantity;
49privatedouble price;
50
51publicdoublegetTotalPrice(){
52return price * quantity;
53}
54
55// 其他與訂單項相關(guān)的業(yè)務(wù)邏輯
56}
57
58publicclassCustomer{
59privateString customerId;
60privateString name;
61privateString email;
62
63// 客戶相關(guān)業(yè)務(wù)邏輯
64}
65
66publicclassPaymentMethod{
67privateString paymentDetails;
68
69publicbooleanisValid(){
70// 驗證支付方法邏輯
71returntrue;
72}
73
74publicvoidcharge(double amount){
75// 處理支付邏輯
76}
77
78// 其他與支付方法相關(guān)的業(yè)務(wù)邏輯
79}
80
81publicclassOrderService{
82privateList<Order> orders;
83
84publicvoidplaceOrder(Order order){
85 orders.add(order);
86 order.processPayment();
87}
88
89publicvoidcancelOrder(Order order){
90 order.setStatus(OrderStatus.CANCELED);
91}
92}
93
94publicenumOrderStatus{
95PENDING,COMPLETED,CANCELED,FAILED
96}
7、高內(nèi)聚實現(xiàn)方式
高內(nèi)聚是軟件設(shè)計中追求的目標,它意味著一個模塊、類或函數(shù)集中于完成一個單一的任務(wù)或職責。
- 單一職責原則(Single Responsibility Principle, SRP)
一個類應(yīng)該只有一個引起它變化的原因。這樣,當需求變化時,只有相關(guān)的類需要修改。
- 相關(guān)功能封裝
將相關(guān)的數(shù)據(jù)和行為封裝在同一個類中,使得類成為其數(shù)據(jù)的“專家”,從而提高內(nèi)聚性。
- 最小化接口暴露
只暴露必要的操作和數(shù)據(jù),減少類與外部的交互,使得類的內(nèi)部操作更加緊密相關(guān)。
- 避免過長的類或方法
過長的類或方法往往承擔了過多的職責,難以維護。將它們拆分成更小的單元可以提高內(nèi)聚性。
- 使用內(nèi)聯(lián)方法(Inline Method)
如果一個方法只在一個類中被調(diào)用,可以考慮將其內(nèi)聯(lián),以減少方法間的不必要分離。
- 提煉類(Refactoring to Class)
如果一組相關(guān)數(shù)據(jù)和行為分散在多個類中,可以提煉出一個新類來封裝這些相關(guān)元素。
- 增強封裝性
通過限制對內(nèi)部數(shù)據(jù)的直接訪問,提供公共接口來操作數(shù)據(jù),可以增加類的內(nèi)聚性。
- 利用組合而非繼承
組合可以提供更靈活的方式來組織代碼,避免繼承帶來的復雜性和緊密耦合。
- 模塊化設(shè)計
將系統(tǒng)分解為多個模塊,每個模塊負責系統(tǒng)的一個特定方面,減少了模塊間的依賴。
- 職責鏈模式(Chain of Responsibility Pattern)
通過創(chuàng)建一系列處理者對象來處理請求,每個處理者只處理它負責的請求類型,從而提高內(nèi)聚性。
- 策略模式(Strategy Pattern)
定義一系列算法,并將每一個算法封裝起來,使它們可以互換,算法的變化不會影響到使用算法的客戶。
- 狀態(tài)模式(State Pattern)
允許一個對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為,從而將相關(guān)的變更邏輯封裝在類內(nèi)部。
- 聚合關(guān)系(Aggregation)
使用聚合關(guān)系來表示整體與部分的關(guān)系,但部分可以獨立于整體存在,這有助于保持類的職責單一和內(nèi)聚。
- 服務(wù)類(Utility Classes)
對于提供通用功能但與業(yè)務(wù)邏輯無關(guān)的類,可以將其設(shè)計為服務(wù)類,這些類通常包含靜態(tài)方法,用于執(zhí)行特定的輔助任務(wù)。
- 領(lǐng)域驅(qū)動設(shè)計(Domain-Driven Design, DDD)
通過DDD的戰(zhàn)術(shù)設(shè)計模式,如實體(Entity)、值對象(Value Object)、聚合(Aggregate)、領(lǐng)域服務(wù)(Domain Service)等,來提高模型的內(nèi)聚性。
- 模塊化編程(Modular Programming)
將程序分解為可重用的模塊,每個模塊包含一組相關(guān)的功能,減少模塊間的依賴。
- 依賴注入(Dependency Injection)
使用依賴注入來降低組件之間的耦合度,提高組件的內(nèi)聚性,因為組件不再負責其依賴的創(chuàng)建和管理。
- 策略枚舉(Strategy Enums)
在某些語言中,可以使用枚舉來實現(xiàn)策略模式,每個枚舉實例代表一個策略,這樣可以將策略的變體封裝在一個小的、內(nèi)聚的范圍內(nèi)。
8、高內(nèi)聚(High Cohesion)與充血模型(Rich Domain Model)區(qū)別:
高內(nèi)聚
- 定義:高內(nèi)聚是指一個模塊或類中的元素彼此之間功能相關(guān)的程度高,每個模塊或類負責一項任務(wù)或職責。
- 目的:高內(nèi)聚的目的是確保代碼的可讀性、可維護性和可重用性。它通過集中相關(guān)功能來減少模塊間的交互。
- 應(yīng)用:通常在設(shè)計單個類或模塊時使用,強調(diào)一個類應(yīng)該只有一個改變的原因。
充血模型
- 定義:充血模型是一種軟件設(shè)計模式,其中領(lǐng)域?qū)ο蟛粌H包含數(shù)據(jù),還包含操作這些數(shù)據(jù)的行為和邏輯。
- 目的:充血模型旨在創(chuàng)建一個高度模塊化和可維護的系統(tǒng),領(lǐng)域?qū)ο筘撠熥约旱男袨楹蜖顟B(tài),提高了系統(tǒng)的靈活性和可維護性。
- 應(yīng)用:通常應(yīng)用于整個系統(tǒng)的架構(gòu)設(shè)計,強調(diào)領(lǐng)域?qū)ο蟮淖宰阈院蜆I(yè)務(wù)邏輯的封裝。
主要區(qū)別
- 設(shè)計層面:
- 高內(nèi)聚原則更側(cè)重于單個類或模塊的設(shè)計,確保它們功能集中。
- 充血模型更側(cè)重于系統(tǒng)架構(gòu)層面,強調(diào)整個系統(tǒng)的模塊化和領(lǐng)域?qū)ο蟮淖宰阈浴?/span>
- 設(shè)計哲學:
- 高內(nèi)聚是一種設(shè)計原則,關(guān)注于減少類內(nèi)部元素的多樣性,確保它們緊密相關(guān)。
- 充血模型是一種設(shè)計模式,關(guān)注于領(lǐng)域?qū)ο蟮耐暾院妥宰阈裕_保它們能夠獨立地執(zhí)行業(yè)務(wù)邏輯。
- 實現(xiàn)方式
- 高內(nèi)聚通過確保類或模塊的功能緊密相關(guān)來實現(xiàn)。
- 充血模型通過將業(yè)務(wù)邏輯和狀態(tài)管理封裝在領(lǐng)域?qū)ο髢?nèi)部來實現(xiàn)。
- 目的
- 高內(nèi)聚原則的目的是提高代碼的內(nèi)聚性和可維護性。
- 充血模型的目的是創(chuàng)建一個高度模塊化和可維護的系統(tǒng),使得領(lǐng)域?qū)ο竽軌颡毩⒌貓?zhí)行業(yè)務(wù)邏輯。
9. 參考開源框架
許多領(lǐng)域驅(qū)動設(shè)計(DDD)框架,如Axon Framework和Apache Isis,都遵循高內(nèi)聚原則來組織代碼。
10. 總結(jié)
高內(nèi)聚原則是GRASP原則中的一個重要組成部分,它通過將數(shù)據(jù)和行為封裝在同一個類中,提高了代碼的內(nèi)聚性和可維護性。遵循高內(nèi)聚原則有助于創(chuàng)建一個清晰、直觀的系統(tǒng)結(jié)構(gòu),簡化了數(shù)據(jù)管理。雖然在某些情況下可能需要權(quán)衡封裝的程度。