思想概要架構
裝飾模式是對里氏替換的一種擴充,裏式替換原則,即:基類能夠出現的地方均可以替換爲子類。這彷佛就是」繼承「的標準定義嘛,可是咱們的前輩經常告誡咱們,繼承是危險的,要謹慎使用繼承,除非你可以證實A is B,而不是A like B。對於全部A like B的地方,都要積極的使用組合,而不是繼承。繼承之因此不受待見,是由於它破壞了封閉原則。子類不只繼承了基類的public函數,同時它也能夠訪問基類的protected函數,而這些函數本質上是包封閉的,開發者們不會對封閉函數的一致性負責,一旦基類在從此的某次升級中被改變,那子類就頗有可能不能正常運行。既然不推薦使用繼承,那對已有類的功能增強就落在了以組合爲基本架構的裝飾模式上。組合的好處是你沒法訪問public之外的函數,能夠充分享受public函數擁有的一致性福利,不用擔憂隨着版本的不一樣而功能失效。ide
依舊是盜圖,請原諒。上圖展現了裝飾者模式的UML結構,真實類爲ConcreteComponent,基礎裝飾者爲Decorator,相似於代理模式,裝飾者也是要調用真實類的API來實現功能。可是就像咱們以前所說的同樣,裝飾者不是真實類的子類,它不經過繼承來擴展功能,而是組合了真實類,同時,爲了實現外部接口的一致性,它實現了Componet接口,外部代碼能夠像使用真實類同樣使用裝飾者。函數
不一樣於代理模式,代理模式的使用者們不太在乎代理類作了哪些事,由於代理者自己不擴展核心功能,而使用裝飾者模式的用戶更多的是強調裝飾者們擴展了哪些功能,他們更在乎裝飾者的存在。因此在類圖裏你能夠看到,除了基本裝飾者(Decorator)外,還有繼承自基礎裝飾者的ConcreteDecoratorA和ConcreteDecoratorB,由於它們各自擴展不一樣的功能,因此會分化出各類類,而在代理模式中,你幾乎不會看到代理者還有子類。spa
講到裝飾者模式的具體事例,永遠逃不掉加糖加奶咖啡的例子,很無聊可是頗有表明性,你值得看看。3d
咖啡能夠加糖也能夠不加糖,能夠加奶也能夠不加奶,固然也能夠既加糖又加奶。這個問題的着眼點其實已經不是咖啡了,它再也不重要,重要的是糖和奶這兩個裝飾屬性。這就是上面提到的,使用裝飾者的客戶每每不在意真實類的功能,他們更在意裝飾者擴展了哪些內容。咱們經過具體代碼來看看它們是如何工做的:代理
public interface ICafe { /* API */ void getCafe(); }
這是咖啡接口,只有」要咖啡「這個基本API。code
public class ConcreteCafe implements ICafe { @Override public void getCafe() { System.out.println("Make cafe"); } }
這是原味咖啡製做類,僅僅作一杯黑咖啡blog
public class ConcreteDecorator implements ICafe { private final ICafe cafe; public ConcreteDecorator(ICafe ref) { cafe = ref; } @Override public void getCafe() { cafe.getCafe(); } }
裝飾者基類,沒有追加任何功能,僅僅調用接口類的共有函數。繼承
public class SugarDecorator extends ConcreteDecorator { public SugarDecorator(ICafe ref) { super(ref); } @Override public void getCafe() { super.getCafe(); System.out.println("Add sugar"); } }
加糖咖啡類,僅僅是在作咖啡以後加了一塊糖接口
public class MilkDecorator extends ConcreteDecorator { public MilkDecorator(ICafe ref) { super(ref); } @Override public void getCafe() { super.getCafe(); System.out.println("Add milk"); } }
加奶咖啡,僅僅是在作咖啡後加了點奶
public class Launcher { public static void main(String[] args) { ICafe cafe = new MilkDecorator(new SugarDecorator(new ConcreteCafe())); cafe.getCafe(); } }
檢驗結果的客戶代碼。它要了杯加糖又加奶的咖啡,運行結果是:
Make cafe
Add sugar
Add milk
整體來講,裝飾類比較好掌握,不斷的對基礎類的裝飾能夠獲得複雜的功能,這比修改實際代碼更快或更好。