Java經(jīng)典設(shè)計模式之裝飾器模式解析
裝飾器模式
裝飾器模式主要解決繼承關(guān)系過于復(fù)雜的問題,通過組合來替代繼承。
指在不改變現(xiàn)有對象結(jié)構(gòu)的情況下,動態(tài)地給該對象增加一些職責(zé)(即增加其額外功能)的模式,裝飾器模式提供了比繼承更有彈性的替代方案將功能附加到對象上。
因此,裝飾器模式的核心功能是功能擴展,使用裝飾器模式可以透明且動態(tài)的擴展類的功能。
裝飾器模式還有一個特點,那就是可以對原始類嵌套使用多個裝飾器。
為了滿足這個應(yīng)用場景,在設(shè)計的時候,裝飾器類需要跟原始類繼承相同的抽象類或者接口。
應(yīng)用場景:
- 用于擴展一個類的功能,或者給一個類添加附加職責(zé)
- 動態(tài)的給一個對象添加功能,這些功能可以再動態(tài)的被撤銷
- 需要為一批平行的兄弟類進行改裝或者加裝功能
UML類圖
由上圖可知,裝飾器模式主要包含了4個角色;
- 抽象組建(Component):可以是一個接口或者抽象類,充當(dāng)被裝飾類的原始對象,規(guī)定了被裝飾對象的行為
- 具體組件(ConcreteComponent):實現(xiàn)/繼承Component的一個具體對象,即被裝飾對象
- 抽象裝飾器(Decorator):通用的裝飾ConcreteComponent的裝飾器,其內(nèi)部必然有一個屬性指向Component,其實現(xiàn)一般是一個抽象類,主要為了讓其子類按照其構(gòu)造形式傳入一個Component,這是強制的通用行為。如果系統(tǒng)中裝飾邏輯單一,則并不需要實現(xiàn)許多裝飾器,可以直接省略該類,而直接實現(xiàn)一個具體裝飾器即可
- 具體裝飾器(ConcreteDecorator):Decorator的具體實現(xiàn)類,理論上,每個ConcreteDecorator都擴展了Component對象的一種功能
裝飾器模式的實現(xiàn)原理就是,讓裝飾器實現(xiàn)與被裝飾類相同的接口,使得裝飾器與被擴展類類型一致,并在構(gòu)造函數(shù)中傳入該接口對象,然后再實現(xiàn)這個接口的被 包裝類 屬于同一類型,且構(gòu)造函數(shù)的參數(shù)為其實現(xiàn)接口類,因此裝飾器模式具備嵌套擴展功能,這樣就能使用裝飾器模式一層一層的對底層被包裝類進行功能擴展了
通用寫法
public class Client { public static void main(String[] args) { ConcreteComponent c1 = new ConcreteComponent(); ConcreteDecoratorA decoratorA = new ConcreteDecoratorA(c1); decoratorA.operation(); System.out.println("---------------------------"); ConcreteDecoratorB decoratorB = new ConcreteDecoratorB(c1); decoratorB.operation(); System.out.println("---------------------------"); ConcreteDecoratorB decoratorB1 = new ConcreteDecoratorB(decoratorA); decoratorB1.operation(); } static abstract class Component{ public abstract void operation(); } static class ConcreteComponent extends Component{ @Override public void operation() { System.out.println("處理業(yè)務(wù)邏輯??!"); } } static abstract class Decorator extends Component{ protected Component component; public Decorator(Component component){ this.component = component; } public void operation(){ //轉(zhuǎn)發(fā)請求給組建對象,可以在轉(zhuǎn)發(fā)前后執(zhí)行一些附加動作 component.operation(); } } static class ConcreteDecoratorA extends Decorator{ public ConcreteDecoratorA(Component component) { super(component); } private void operationFirst(){ System.out.println("ConcreteDecoratorA裝飾operationFirst"); } private void operationLast(){ System.out.println("ConcreteDecoratorA裝飾operationLast"); } public void operation(){ operationFirst(); super.operation(); operationLast(); } } static class ConcreteDecoratorB extends Decorator{ public ConcreteDecoratorB(Component component) { super(component); } private void operationFirst(){ System.out.println("ConcreteDecoratorB裝飾operationFirst"); } private void operationLast(){ System.out.println("ConcreteDecoratorB裝飾operationLast"); } public void operation(){ operationFirst(); super.operation(); operationLast(); } } }
示例:使用裝飾器模式解決煎餅加碼問題
下面用代碼來模擬給煎餅加碼的業(yè)務(wù)場景,先來看不用裝飾器模式的情況。
首先創(chuàng)建一個煎餅Battercake類
public class Battercake { protected String getMsg(){ return "煎餅"; } public int getPrice(){ return 5; } }
然后創(chuàng)建一個加雞蛋的煎餅BattercakeWithEgg類
public class BattercakeWithEgg extends Battercake{ protected String getMsg(){ return super.getMsg() + "+ 1個雞蛋"; } public int getPrice(){ return super.getPrice() + 1; } }
在創(chuàng)建一個既加雞蛋又加香腸的BattercakeWithEggAndSausage類
public class BattercakeWithEggAndSausage extends BattercakeWithEgg{ protected String getMsg(){ return super.getMsg() + "+ 1根香腸"; } public int getPrice(){ return super.getPrice() + 2; } }
最后編寫客戶端測試代碼
public class ClientTest { public static void main(String[] args) { Battercake battercake = new Battercake(); System.out.println(battercake.getMsg() + ",總價格" + battercake.getPrice()); BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg(); System.out.println(battercakeWithEgg.getMsg() + ",總價格" + battercakeWithEgg.getPrice()); BattercakeWithEggAndSausage battercakeWithEggAndSausage = new BattercakeWithEggAndSausage(); System.out.println(battercakeWithEggAndSausage.getMsg() + ",總價格" + battercakeWithEggAndSausage.getPrice()); } }
運行結(jié)果如下:
煎餅,總價格5
煎餅+ 1個雞蛋,總價格6
煎餅+ 1個雞蛋+ 1根香腸,總價格8
運行結(jié)果沒有問題。
但是,如果用戶需要一個加2個雞蛋和1根香腸的煎餅,則用現(xiàn)在的類結(jié)構(gòu)是創(chuàng)建不出來的,也無法自動計算出價格,除非再創(chuàng)建一個類做定制。
如果需求在變,那么一直加定制顯然是不科學(xué)的
下面用裝飾器模式來解決上面的問題。首先創(chuàng)建一個煎餅的抽象Battercake類。
public abstract class Battercake { protected abstract String getMsg(); protected abstract int getPrice(); }
創(chuàng)建一個基本的煎餅(或者叫基礎(chǔ)套餐)BaseBattercake.
public class BaseBattercake extends Battercake{ @Override protected String getMsg() { return "煎餅"; } @Override protected int getPrice() { return 5; } }
然后創(chuàng)建一個擴張?zhí)撞偷某橄笱b飾器BattercakeDecotator類
public abstract class BattercakeDecotator extends Battercake{ private Battercake battercake; public BattercakeDecotator(Battercake battercake){ this.battercake = battercake; } protected abstract void doSomething(); protected String getMsg(){ return this.battercake.getMsg(); } protected int getPrice(){ return this.battercake.getPrice(); } }
接著創(chuàng)建雞蛋裝飾器EggDecorator
public class EggDecorator extends BattercakeDecotator{ public EggDecorator(Battercake battercake) { super(battercake); } @Override protected void doSomething() { } protected String getMsg(){ return super.getMsg() + "+1個雞蛋"; } protected int getPrice(){ return super.getPrice() + 1; } }
創(chuàng)建香腸裝飾器SausageDecorator類
public class SausageDecorator extends BattercakeDecotator{ public SausageDecorator(Battercake battercake) { super(battercake); } @Override protected void doSomething() { } protected String getMsg(){ return super.getMsg() + "+1根香腸"; } protected int getPrice(){ return super.getPrice() + 2; } }
在編寫客戶端測試代碼
public class Client { public static void main(String[] args) { //買一個煎餅 Battercake battercake; battercake = new BaseBattercake(); //煎餅有點小,想再加1個雞蛋 battercake = new EggDecorator(battercake); battercake = new EggDecorator(battercake); battercake = new SausageDecorator(battercake); System.out.println(battercake.getMsg() + ",總價: " + battercake.getPrice()); } }
運行結(jié)果如下圖所示:
煎餅+1個雞蛋+1個雞蛋+1根香腸,總價: 9
裝飾器模式與代理模式區(qū)別
從代理模式的UML類圖和通用代碼實現(xiàn)上看,代理模式與裝飾器模式幾乎一摸一樣。
代理模式的Subject對應(yīng)裝飾器模式的Component,代理模式的RealSubject對應(yīng)裝飾器模式的Concrete Component,代理模式的Proxy對應(yīng)的裝飾器模式的Decorator。
確實,從代碼實現(xiàn)上看,代理模式的確與裝飾器模式是一樣的,但是這兩種設(shè)計模式多面向的功能擴展面是不一樣的。
裝飾器模式強調(diào)自身功能的擴展。
Decorator所做的就是增強ConcreteComponent的功能,主體對象為ConcreteComponent,著重類功能的變化。
代理模式強調(diào)對代理過程的控制。
Proxy完全掌握對RealSubject的訪問控制,因此,Proxy可以決定對RealSubject進行功能擴展,功能縮減甚至功能散失,主體對象為Proxy。
裝飾器模式優(yōu)缺點
優(yōu)點:
- 裝飾器是繼承的有力補充,比繼承靈活,在不改變原有對象的情況下,動態(tài)地給一個對象擴展功能,即插即用。
- 通過使用不同裝飾類及這些裝飾類的排列組合,可以實現(xiàn)不同效果
- 裝飾器模式完全遵守開閉原則
缺點:
- 會出現(xiàn)更多的代碼,更多的類,增加程序的復(fù)雜性
- 動態(tài)裝飾在多層裝飾時會更復(fù)雜
到此這篇關(guān)于Java經(jīng)典設(shè)計模式之裝飾器模式解析的文章就介紹到這了,更多相關(guān)Java裝飾器模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java將RTF文檔轉(zhuǎn)換為Word/PDF/HTML/圖片
RTF文檔因其跨平臺兼容性而廣泛使用,但有時在不同的應(yīng)用場景可能需要特定的文檔格式,所以本文來講講如何通過Java將RTF轉(zhuǎn)換為Word/PDF/HTML和圖片格式2025-01-01Mybatis工具類JdbcTypeInterceptor運行時自動添加jdbcType屬性
今天小編就為大家分享一篇關(guān)于Mybatis工具類JdbcTypeInterceptor運行時自動添加jdbcType屬性,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12SpringBoot深入理解之內(nèi)置web容器及配置的總結(jié)
今天小編就為大家分享一篇關(guān)于SpringBoot深入理解之內(nèi)置web容器及配置的總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03SpringBoot接口數(shù)據(jù)加解密實戰(zhàn)記錄
現(xiàn)今對于大多數(shù)公司來說,信息安全工作尤為重要,下面這篇文章主要給大家介紹了關(guān)于SpringBoot接口數(shù)據(jù)加解密的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-07-07代理角色java設(shè)計模式之靜態(tài)代理詳細介紹
查了好多資料,發(fā)現(xiàn)還是不全,干脆自己整理吧,至少保證在我的做法正確的,以免誤導(dǎo)讀者,也是給自己做個記錄吧!2013-05-05使用IDEA啟動項目遇見ClassNotFoundException的解決方案
這篇文章主要介紹了使用IDEA啟動項目遇見ClassNotFoundException的正確解決方案,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06Java使用split分割無效獲取不到預(yù)期效果的解決辦法
這篇文章主要給大家介紹了關(guān)于Java使用split分割無效獲取不到預(yù)期效果的解決辦法,java的String類中有個split方法,這個是我們經(jīng)常使用到的,需要的朋友可以參考下2023-08-08