設計模式第四篇,裝飾者模式,大家多多指教。
簡介
裝飾者模式是動態的將責任附加到對象上(引自《Head First設計模式》)。這里的重點在於動態這兩個字,我們都知道繼承的實現的方式,它是是類編譯的時候就去加載文件,屬於一種靜態的附加,而我們要實現動態的附加就不能單純的通過繼承來實現。在這種背景下,裝飾者模式就應運而生了。裝飾者模式的實現:首先所有的類都有一個共同的抽象,這個抽象可以是一個抽象類,也可以是一個接口,所有的類該抽象的子類或者實現。語言描述比較抽象,下面我們通過一個例子來描述該模式。
例子
我們舉一個吃火鍋的例子,根據裝飾者模式的特點:
首先我們要有個共同的抽象,這里我們通過接口來實現,這個接口我們稱之為鍋底。
/* * 鍋底 */ public interface Hotpot { /** 描述 */ String getName(); /** 價格 */ Double getPrice(); }
第二步,我們設計具體的鍋底類,我們分為醬香鍋、香辣鍋和清湯鍋,這是底鍋,每筆單都必須有且只有一個。
public class JamHotpot implements Hotpot{ @Override public String getName() { return "醬香鍋"; } @Override public Double getPrice() { return 4.0; } } public class SpicyHotpot implements Hotpot{ @Override public String getName() { return "香辣鍋"; } @Override public Double getPrice() { return 4.0; } } public class ClearSoupHotpot implements Hotpot{ @Override public String getName() { return "清湯鍋"; } @Override public Double getPrice() { return 3.0; } }
第三步,我們新增一個配菜接口,繼承共同的接口鍋底,這個接口用來作為所有配菜的父級,該接口可以拓展所以配菜的共有屬性
/* * 配菜 */ public interface SideDish extends Hotpot { //可拓展配菜的公共屬性 }
第四步,我們需要新增具體的配菜類,這里的配菜類我們注意到比鍋底類多了成員變量和構造方法,而且getName和getPrice方法多了加了傳入參數,這個就是為了動態的去附加功能,具體怎么實現參考最后一步。
/* * 牛肉 */ public class Beef implements SideDish { private Hotpot hotpot; public Beef(Hotpot hotpot) { this.hotpot = hotpot; } @Override public String getName() { return hotpot.getName() + ",牛肉"; } @Override public Double getPrice() { return hotpot.getPrice() + 38.0; } } /* * 生菜 */ public class Lettuce implements SideDish { private Hotpot hotpot; public Lettuce(Hotpot hotpot) { this.hotpot = hotpot; } @Override public String getName() { return hotpot.getName() + ",生菜"; } @Override public Double getPrice() { return hotpot.getPrice() + 8.0; } }
最后我們測試:
public class DecoratorTest { public static void main(String[] args) { System.out.println("=======醬香鍋底加牛肉"); Hotpot hotpot = new JamHotpot(); hotpot = new Beef(hotpot); hotpot = new Lettuce(hotpot); System.out.println("===火鍋的價格為:" + hotpot.getPrice() + "元"); //如果我們需要更多的配菜參考上面的方式去添加更多的配菜 } }
總結
如上面所描寫的那樣,我只需要前台傳遞給我們所點的鍋底和配菜,我們后台去動態的生成一個新的火鍋對象。這么設計的好處的拓展極其方便,就算這個火鍋店加了新的鍋底,或者是配菜,對我們之前的代碼是沒有任何影響的,這就是所謂的開閉原則(對拓展開放,對修改關閉),后面我們會講到設計模式相應的原則。除了上面的好處之外,裝飾者模式也有他本身不能忽視的問題,那就是每次生成一筆訂單都會生成很多小對象,如果該筆單對象很多的話,對我們的內存是個不小的考驗,而且過多的小類會讓我們的系統變得很復雜,讓人很難以理解。所以裝飾器模式有利有弊(更多的是利大於弊),我們可以好好思考裝飾者模式應用場景,然后進一步掌握這種設計思想。
番外
想了解裝飾者模式的更多詳細信息可以通過jdk源碼或者spring源碼去了解。jdk中IO操作就有通過裝飾者模式實現的,FilterInputStream和它的實現就是一個裝飾者模式,同樣的在spring源碼中,凡事以Decorator結尾的類都是實現的裝飾者模式,當然不是所有的裝飾器模式都是以Decorator結尾的。