一、裝飾模式
1、概述
動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。
- ConcreteComponent :是定義了一個具體的對象,可以給這個對象添加一些職責;
- Decorator :裝飾抽象類,繼承了Component,從外部類來擴展Component類的功能,但對于Component來說,無需知道Decorator的存在的;
- ConcerteDecorator?:就是具體的裝飾對象,起到給Component添加職責的功能;
2、代碼演示模塊:?
我們這里就用給一個對象穿衣服的例子,來說明裝飾模式:
(1)Person類(Component)
public class Person {private String name;public Person() {}public Person(String name) {this.name = name;}public void show() {System.out.println("裝扮的"+ name);}
}
(2)飾類(Decorator)
public abstract class Finery extends Person {//人對象作為屬性private Person component;//get和set方法public Person getComponent() {return component;}public void setComponent(Person component) {this.component = component;}//重寫父類的某個功能public void show(){if (component != null){component.show();}}
}
(3)具體服飾類(ConcreteDecorator)
public class Suit extends Finery {public void show() {System.out.print("T恤");super.show();}
}public class TShirts extends Finery {public void show(){System.out.print("運動褲");super.show();}
}.
.
.public class GymShoes extends Finery {public void show() {System.out.print("球鞋");super.show();}
}
(4)客戶端代碼:
public class test {public static void main(String[] args) {Person person = new Person("某某");System.out.print("第一種裝扮:");Finery ts = new TShirts();Finery bt = new BigTrouser();Finery gs = new GymShoes();ts.setComponent(person);bt.setComponent(ts);gs.setComponent(bt);gs.show();System.out.print("第二種裝扮:");Finery ne = new Necktie();Finery su = new Suit();Finery ls = new LeatherShoes();ne.setComponent(person);su.setComponent(ne);ls.setComponent(su);ls.show();}
}運行結果:
第一種裝扮:球鞋 垮褲 大T恤 裝扮的某某
第二種裝扮:皮鞋 西裝 領帶 裝扮的某某
3、優點
(1)增強功能的靈活性:
? ? ? ?裝飾模式可以在不改變原有對象結構的基礎上,動態地給對象添加新的功能。例如,在一個咖啡店的點單系統中,有一個基礎的咖啡類Coffee
,它有一個方法cost()
用于計算咖啡的價格。通過裝飾模式,可以創建諸如MilkDecorator
(牛奶裝飾器)、SugarDecorator
(糖裝飾器)等裝飾類。這樣,用戶可以根據自己的喜好選擇添加牛奶或糖來裝飾咖啡,而不需要改變Coffee
類本身的結構。如果后續還想添加新的配料,如焦糖、香草等,只需要創建新的裝飾類即可。
?(2)實現多層裝飾組合
? ? ? ? 可以對一個對象進行多層裝飾,以實現復雜的功能組合。比如,對于上面的咖啡示例,可以先使用MilkDecorator
裝飾Coffee
對象,然后再使用SugarDecorator
進行裝飾。這樣就可以得到一杯加了牛奶和糖的咖啡。每一層裝飾都可以獨立地添加自己的功能,并且這些功能會按照裝飾的順序依次疊加。這種組合方式非常靈活,可以滿足各種復雜的業務需求。
(3)符合開閉原則
? ? ? ? ?該模式對擴展開放,對修改關閉。當需要為對象添加新的功能時,不需要修改原有的類,而是通過創建新的裝飾類來實現。這使得系統具有更好的可維護性和可擴展性。以圖形繪制系統為例,有一個基本的圖形類Shape
,它有一個draw()
方法用于繪制圖形。如果要為圖形添加新的繪制效果,如陰影、漸變等,可以通過創建ShadowDecorator
、GradientDecorator
等裝飾類來實現,而不用修改Shape
類的代碼。
4、缺點
? ? ? ? 增強了系統的復雜度,?隨著裝飾類的增多,系統的復雜度會逐漸增加。因為每個裝飾類都要了解被裝飾對象的接口,而且裝飾類之間可能會相互嵌套,這使得代碼的理解和調試變得困難。例如,在一個大型的電商系統中,對商品價格計算功能進行裝飾,可能會有折扣裝飾、運費裝飾、稅費裝飾等多種裝飾類。如果這些裝飾類的邏輯比較復雜,而且相互嵌套,就會給開發人員理解和維護代碼帶來很大的挑戰。
5、總結
(1)裝飾模式是為已有的功能動態的添加更多的功能的一種方式,屬于開閉原則,對擴展開放、修改關閉,降低代碼的耦合性,符合代碼的設計原子低耦合高內聚,減少了代碼的冗余和復雜度,更加靈活的使用每個模塊。
(2)那什么時候適合去使用它呢?
? ? ?正常不使用裝飾模式下: 當系統需要新功能的時候,是向舊的類中添加新的代碼。這些新加的代碼通常裝飾了原有類的核心職責或主要行為,這么做的問題主要是在主類中加入了新的字段,新的方法和新的邏輯,從而增加了主類的復雜度,而這些新加入的東西僅僅只是為了滿足一些只在特定情況下才會執行的特殊行為的需要。
(3)在使用裝飾模式下: 它是把每個要裝飾的功能放在單獨的類中,并讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行為時,客戶代碼就可以在運行時根據需要有選則的按順序使用裝飾功能包裝對象。
二、職責鏈模式
1、概述
? ? ? ? 使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系,將這個對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
Handler :抽象處理請求類:將自身設為自己的屬性,再定義一個解決請求的抽象方法
ConcreteHandler : 具體處理請求類,繼承Handler抽象類,重寫解決請求的方法。每一個的解決請求的方法體可以相互包含,也可以形成互補的方法。可訪問它的后繼者,如果可處理該請求,就處理。否則就將該請求轉發給后繼者;
2、代碼演示模塊
我們處理一組數據,演示職責模式。
(1)Handler類,定義一個處理請求的接口
abstract class Handler {protected Handler successor;public void setSuccessor(Handler successor){//設置繼承者this.successor = successor;};//處理請求的抽象方法public abstract void handleRequest(int request);
}
(2)ConcreteHandler1,當請求數在0-10之間則有權處理,否則轉到下一位
public class ConcreteHandler1 extends Handler{public void handleRequest(int request){//0-10處理請求if(request>=0&&request<=10){System.out.println("ConcreteHandler1 handle request "+request);}else{if(successor!=null){//轉移到下一位successor.handleRequest(request);}}}
}
?(3)ConcreteHandler2,當請求數在10-20之間則有權處理,否則轉到下一位
public class ConcreteHandler2 extends Handler{public void handleRequest(int request){if(request >= 10 && request < 20){System.out.println("ConcreteHandler2 處理請求:" + request);}else if(successor != null){successor.handleRequest(request);}}
}
(4)ConcreteHandler3,請求20-30之間有權處理,否則轉到下一位
public class ConcreteHandler3 extends Handler{public void handleRequest(int request){if(request>=20&&request<30){System.out.println("---由第三個處理者處理---");}else{if(successor!=null){successor.handleRequest(request);}}}
}
(5) 客戶端代碼
public class Start {public static void main(String[] args) {// 創建一個請求int request = 5;// 創建三個處理者Handler handler1 = new ConcreteHandler1();Handler handler2 = new ConcreteHandler2();Handler handler3 = new ConcreteHandler3();// 設置處理者鏈handler1.setSuccessor(handler2);handler2.setSuccessor(handler3);// 處理請求int [] result ={2,5,14,22,18,3,27,20};for (int i=0;i<result.length;i++){handler1.handleRequest(result[i]);}}
}
3、優點
(1)降低了發送者和接收者之間的耦合
? ? ? ?在責任鏈模式中,請求的發送者不需要知道具體是哪個接收者來處理請求。它只需要將請求發送到責任鏈的起始端即可。例如,在一個投訴處理系統中,用戶提交投訴后,投訴請求會沿著預先設定的責任鏈(可能是客服代表 - 客服主管 - 部門經理)傳遞,用戶不需要了解具體是哪個環節會處理他的投訴,這樣就降低了發送者和接收者之間的耦合度。
(2)增強系統的靈活性和可擴展性
新的處理者可以很容易地添加到責任鏈中。
4、缺點
(1)部分請求可能未被處理
? ? ? ? 如果責任鏈沒有正確配置,或者沒有合適的處理者能夠處理某個請求,這個請求可能會一直傳遞到責任鏈的末尾而得不到處理。比如,在一個數據驗證系統中,如果沒有對所有可能的數據類型都配置相應的驗證處理者,那么一些特殊類型的數據可能就無法得到驗證,從而導致數據錯誤進入后續流程。
(2)調試困難
? ? ? ? 由于請求在責任鏈中可能經過多個處理者,當出現問題時,確定是哪個處理者導致的問題可能會比較復雜。例如,在一個復雜的工作流審批系統中,如果一個審批請求出現異常,可能需要逐一檢查責任鏈中的每個審批者的處理邏輯和狀態,才能找到問題所在。
?5、總結
? ? ? ? 責任鏈模式提供了一種靈活的方式來處理請求,使得多個對象可以有機會參與到請求的處理過程中。通過解耦請求發送者和接收者,它增強了系統的可維護性和可擴展性。然而,使用這種模式也需要注意其潛在的缺點,如調試困難、可能出現請求未被處理的情況以及性能問題。