代理模式和裝飾器模式很像,這里用【到咖啡館喝咖啡】作例子來講解。
基礎實現
定義一個咖啡的接口。
public interface Coffee { /** * 打印當前咖啡里有什么 */ void printMaterial(); }
定義一個苦咖啡的實現。
public class BitterCoffee implements Coffee { @Override public void printMaterial() { System.out.println("咖啡"); } }
定義一個默認的點咖啡邏輯。
@Test public void orderCoffee { Coffee coffee = new BitterCoffee(); coffee.printMaterial(); // 咖啡 }
裝飾器模式
你喝了一口咖啡,覺得有點苦,於是你就想加點糖。
定義一個咖啡裝飾器(加糖)。
public class CoffeeDecorator implements Coffee { /** * 持有一個咖啡對象 */ private final Coffee coffee; public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } @Override public void printMaterial() { System.out.println("糖"); this.coffee.printMaterial(); } }
定義一個咖啡加糖的應用邏輯。
@Test public void addSugerIntoCoffee { Coffee coffee = new BitterCoffee(); // 點了一杯苦咖啡 coffee = new SugarDecorator(coffee); // 給咖啡加了點糖 coffee.printMaterial(); // 糖 咖啡 }
裝飾器適用場景:我有一個對象,但是這個對象的功能不能使我滿意(咖啡太苦了),我就拿裝飾器給他裝飾一下(給咖啡加糖)。
代理模式(靜態代理)
約好的朋友來了,要給她點一杯咖啡,你知道咖啡很苦,決定直接點一杯加了糖的咖啡給她。
定義一個加糖咖啡的類。
public class CoffeeProxy implements Coffee { private final Coffee coffee; public CoffeeProxy() { this.coffee = new BitterCoffee(); } @Override public void printMaterial() { System.out.println("糖"); this.coffee.printMaterial(); } }
然后定義一個點加糖咖啡的邏輯。
@Test public void addSugerIntoCoffee { Coffee coffee = new CoffeeProxy(); coffee.printMaterial(); // 糖 咖啡 }
裝飾器和代理模式的區別
對裝飾器模式來說,裝飾者(Decorator)和被裝飾者(Decoratee)都實現一個接口。對代理模式來說,代理類(Proxy Class)和真實處理的類(Real Class)都實現同一個接口。此外,不論我們使用哪一個模式,都可以很容易地在真實對象的方法前面或者后面加上自定義的方法。
在上面的例子中,裝飾器模式是使用的調用者從外部傳入的被裝飾對象(coffee),調用者只想要你把他給你的對象裝飾(加強)一下。而代理模式使用的是代理對象在自己的構造方法里面new的一個被代理的對象,不是調用者傳入的。調用者不知道你找了其他人,他也不關心這些事,只要你把事情做對了即可。
裝飾器模式關注於在一個對象上動態地添加方法,而代理模式關注於控制對對象的訪問。換句話說,用代理模式,代理類可以對它的客戶隱藏一個對象的具體信息。因此當使用代理模式的時候,我們常常在一個代理類中創建一個對象的實例;當使用裝飾器模式的時候,我們通常的做法是將原始對象作為一個參數傳給裝飾器的構造器。
裝飾器模式和代理模式的使用場景不一樣,比如IO流使用的是裝飾者模式,可以層層增加功能。而代理模式則一般是用於增加特殊的功能,有些動態代理不支持多層嵌套。
代理和裝飾其實從另一個角度更容易去理解兩個模式的區別:代理更多的是強調對對象的訪問控制,比如說,訪問A對象的查詢功能時,訪問B對象的更新功能時,訪問C對象的刪除功能時,都需要判斷對象是否登陸,那么我需要將判斷用戶是否登陸的功能抽提出來,並對A對象、B對象和C對象進行代理,使訪問它們時都需要去判斷用戶是否登陸,簡單地說就是將某個控制訪問權限應用到多個對象上;而裝飾器更多的強調給對象加強功能,比如說要給只會唱歌的A對象添加跳舞功能,添加說唱功能等,簡單地說就是將多個功能附加在一個對象上。
所以,代理模式注重的是對對象的某一功能的流程把控和輔助,它可以控制對象做某些事,重心是為了借用對象的功能完成某一流程,而非對象功能如何。而裝飾模式注重的是對對象功能的擴展,不關心外界如何調用,只注重對對象功能加強,裝飾后還是對象本身。
總結
對於代理類,如何調用對象的某一功能是思考重點,而不需要兼顧對象的所有功能;對於裝飾類,如何擴展對象的某一功能是思考重點,同時也需要兼顧對象的其他功能,因為再怎么裝飾,本質也是對象本身,要擔負起對象應有的職責,被裝飾者的職責一旦增加,作為裝飾類也需要有相應的擴展,必然會造成編碼的負擔。
設計模式本身是為了提升代碼的可擴展性,靈活應用即可,不必生搬硬套,非要分出個所以然來,裝飾器模式和代理模式的區別也是如此。
"我們提着過去,走向人群。"