|
您打算設計一個點餐程式,目前主餐有炸雞、漢堡,您打算讓點了主餐的客入選擇附餐時可以有優惠,如果您使用繼承的方式來達到這個目的,例如: class FriedChicken { double price() { return 49.0; } } class SideDishOne extends FriedChicken { double price() { return super.price() + 30.0; } } 在使用繼承時,多想一下這個問題是否只能用繼承來解決總是好的。以這個設計為例,您繼承父類別之後,只是取得父類別的price()執行結果再進一步加以處理,另一方面,如果漢堡也想要搭配附餐一,目前的SideDishOne顯然無法給漢堡重用,您還得為漢堡建立有附餐一的子類別。 如果採取以下的設計,可以解決問題: ![]() 這個設計不採取繼承而改以組合的方式,如果以Java來示範如何為不同的主餐增加附餐的話,如下所示: interface Meal {各種SideDish的實現並不改變Meal實作本來的操作功能,而是基於原本的操作功能再增加處理,SideDish的各種實現,可以套用至Meal的各種實作,例如FriedChicken或Hambergur。 這是Decorator模式的實現,其不採取繼承的方式,而以組合的方式動態地為物件添加功能。 以Python來實現的話: class FriedChicken: 以UML來表示Decorator模式之結構: ![]() 在Java Swing中的JTextArea元件預設並沒有捲軸,捲 軸的功能是由JScrollPane元件提供,如果您要加入一個具有捲軸功能的JTextArea,您可以如下進行設計: JTextArea textArea = new JTextArea();
JScrollPane scrollPane = new JScrollPane(textArea); 像這樣動態地為JTextArea加入功能的方法,也是Decorator模式的實現,您不用修改JTextArea的功能,也不用使用繼承來擴充JTextArea,對JTextArea來說,JScrollPane就好像是一個捲軸外框,直接套 在JTextArea上作裝飾,就好比您在照片上加上一個相框的意思。 在Gof的書中指出另一個範例,它設計一個Stream抽象類,而有一個StreamDecorator類,Stream的子類有處理記憶體串流的 MemoryStream與FileStream,有各種方法可以處理串流,也許只是單純的處理字元,也許會進行壓縮,也許會進行字元轉換,最基本的處理 可能是處理字元,而字元壓縮被視為額外的功能,這個時候我們可以使用裝飾模式,在需要的時候為Stream物件加上必要的功能,事實上在java.io中 的許多輸入輸出物件,就是採取這樣的設計,例如: BufferedReader reader = new BufferedReader(new FileReader("Main.java")); FileReader 沒有緩衝區處理的功能,所以由BufferedReader來提供,BufferedReader並沒有改變FileReader的功能,而是在既有 FileReader的操作上再作加工的動作,而BufferedReader也不只可以用於FileReader,只要是Reader的子類別,都可以 套用BufferedReader,例如讀取使用者輸入時: BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 透過適當地設計,Decorator角色的類別,也可以重用於適當的元件。 |