詳解Java如何通過裝飾器模式擴展系統(tǒng)功能
盡管目前房價依舊很高,買房的人依舊很多。如果大家買的是毛坯房,無疑還有一項艱巨的任務(wù)要面對,那就是裝修。對新房進行裝修并沒有改變房屋用于居住的本質(zhì),但它可以讓房子變得更漂亮、更溫馨、更實用、更能滿足居家的需求。在軟件設(shè)計中,我們也有一種類似新房裝修的技術(shù)可以對已有對象(新房)的功能進行擴展(裝修),以獲得更加符合用戶需求的對象,使得對象具有更加強大的功能。這種技術(shù)對應(yīng)于一種被稱之為裝飾模式的設(shè)計模式,本章將介紹用于擴展系統(tǒng)功能的裝飾模式。
裝飾器模式概述
裝飾模式可以在不改變一個對象本身功能的基礎(chǔ)上給對象增加額外的新行為,在現(xiàn)實生活中,這種情況也到處存在,例如一張照片,我們可以不改變照片本身,給它增加一個相框,使得它具有防潮的功能,而且用戶可以根據(jù)需要給它增加不同類型的相框,甚至可以在一個小相框的外面再套一個大相框。
裝飾模式是一種用于替代繼承的技術(shù),它通過一種無須定義子類的方式來給對象動態(tài)增加職責,使用對象之間的關(guān)聯(lián)關(guān)系取代類之間的繼承關(guān)系。在裝飾模式中引入了裝飾類,在裝飾類中既可以調(diào)用待裝飾的原有類的方法,還可以增加新的方法,以擴充原有類的功能。
裝飾模式結(jié)構(gòu)圖:
- Component(抽象構(gòu)件):它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實現(xiàn)的業(yè)務(wù)方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對象以及裝飾之后的對象,實現(xiàn)客戶端的透明操作。
- ConcreteComponent(具體構(gòu)件):它是抽象構(gòu)件類的子類,用于定義具體的構(gòu)件對象,實現(xiàn)了在抽象構(gòu)件中聲明的方法,裝飾器可以給它增加額外的職責(方法)。
- Decorator(抽象裝飾類):它也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責,但是具體職責在其子類中實現(xiàn)。它維護一個指向抽象構(gòu)件對象的引用,通過該引用可以調(diào)用裝飾之前構(gòu)件對象的方法,并通過其子類擴展該方法,以達到裝飾的目的。
- ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負責向構(gòu)件添加新的職責。每一個具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴充對象的行為。
由于具體構(gòu)件類和裝飾類都實現(xiàn)了相同的抽象構(gòu)件接口,因此裝飾模式以對客戶透明的方式動態(tài)地給一個對象附加上更多的責任,換言之,客戶端并不會覺得對象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要創(chuàng)造更多子類的情況下,將對象的功能加以擴展。
簡單案例
場景:天氣太熱了,喝點兒冰水解解暑;加點兒檸檬片,讓果汁好喝點兒。
先定義一個喝水的接口
public interface Drink { /** * 喝水 */ void drink(); }
寫一個接口的實現(xiàn)
public class DrinkWater implements Drink { @Override public void drink() { System.out.println("喝水"); } }
一個簡單的裝飾器
public class DrinkDecorator implements Drink { private final Drink drink; public DrinkDecorator(Drink drink) { this.drink = drink; } @Override public void drink() { System.out.println("先加點兒檸檬片"); drink.drink(); } }
測試
public class DrinkMain { public static void main(String[] args) { Drink drink = new DrinkWater(); drink = new DrinkDecorator(drink); drink.drink(); } }
運行結(jié)果
先加點兒檸檬片
喝水
一個簡單的裝飾器模式例子就寫完了,這里只是先了解一下裝飾器模式。
案例場景
大家應(yīng)該都喝過奶茶吧,我經(jīng)常喝的原味奶茶,里面可以添加配料,每增加一種配料,該奶茶的價格就會增加,要求計算一種奶茶的價格。
下面就可以使用裝飾器模式來模擬這種場景。
定義抽象構(gòu)件類
/** * 奶茶 */ public interface MilkyTea { double cost(); }
定義具體構(gòu)件類
/** * 珍珠奶茶 */ public class PearlMilkyTea implements MilkyTea{ @Override public double cost() { return 13.0; } }
定義抽象裝飾類
public abstract class MilkyTeaDecorator implements MilkyTea{ private MilkyTea milkyTea; public MilkyTeaDecorator(MilkyTea milkyTea){ this.milkyTea = milkyTea; } @Override public double cost() { return milkyTea.cost(); } }
定義具體裝飾類
/** * 椰果奶茶 */ public class CoconutDecorator extends MilkyTeaDecorator{ public CoconutDecorator(MilkyTea milkyTea) { super(milkyTea); } @Override public double cost() { System.out.println("添加椰果"); return super.cost() + 2.0; } } /** * 布丁奶茶 */ public class PuddingDecorator extends MilkyTeaDecorator{ public PuddingDecorator(MilkyTea milkyTea) { super(milkyTea); } @Override public double cost() { System.out.println("添加布丁"); return super.cost() + 1.5; } }
測試代碼
public class Client { public static void main(String[] args) { MilkyTea milkyTea = new PearlMilkyTea(); milkyTea = new CoconutDecorator(milkyTea); milkyTea = new PuddingDecorator(milkyTea); System.out.println(milkyTea.cost()); } }
運行結(jié)果
添加布丁
添加椰果
16.5
在客戶端代碼中,我們先定義了一個MilkyTea類型的具體構(gòu)件對象milkyTea
,然后將milkyTea
作為構(gòu)造函數(shù)的參數(shù)注入到具體裝飾類CoconutDecorator中,得到一個裝飾之后對象milkyTea
,再將裝飾了一次之后的milkyTea
對象注入另一個裝飾類PuddingDecorator中實現(xiàn)第二次裝飾,得到一個經(jīng)過兩次裝飾的對象milkyTea,再調(diào)用milkyTea的cost()方法即可得到一杯既加了布丁又加了椰果的珍珠奶茶價格。
裝飾器模式優(yōu)缺點
優(yōu)點
- 裝飾類和被裝飾類可以獨立發(fā)展,不會相互耦合。
- 相比于繼承,更加的輕便、靈活。
- 可以動態(tài)擴展一個實現(xiàn)類的功能,不必修改原本代碼
缺點
- 會產(chǎn)生很多的裝飾類,增加了系統(tǒng)的復雜性。
- 這種比繼承更加靈活機動的特性,也同時意味著裝飾模式比繼承易于出錯,排錯也很困難,對于多次裝飾的對象,調(diào)試時尋找錯誤可能需要逐級排查,較為繁瑣。
裝飾器模式適用場景
在不影響其他對象的情況下,以動態(tài)、透明的方式給單個對象添加職責。
當不能采用繼承的方式對系統(tǒng)進行擴展或者采用繼承不利于系統(tǒng)擴展和維護時可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統(tǒng)中存在大量獨立的擴展,為支持每一種擴展或者擴展之間的組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長;第二類是因為類已定義為不能被繼承(如Java語言中的final類)。
總結(jié)
裝飾模式降低了系統(tǒng)的耦合度,可以動態(tài)增加或刪除對象的職責,并使得需要裝飾的具體構(gòu)件類和具體裝飾類可以獨立變化,以便增加新的具體構(gòu)件類和具體裝飾類。在軟件開發(fā)中,裝飾模式應(yīng)用較為廣泛,例如在JavaIO中的輸入流和輸出流的設(shè)計、javax.swing包中一些圖形界面構(gòu)件功能的增強等地方都運用了裝飾模式。
到此這篇關(guān)于詳解Java如何通過裝飾器模式擴展系統(tǒng)功能的文章就介紹到這了,更多相關(guān)Java裝飾器模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity實現(xiàn)動態(tài)加載權(quán)限信息的方法
這篇文章主要介紹了SpringSecurity實現(xiàn)動態(tài)加載權(quán)限信息,本文給大家介紹的非常詳細,對大家的學習或工作具有一定需要的朋友可以參考下2022-01-01使用dom4j解析xml文件,并轉(zhuǎn)出json格式問題
這篇文章主要介紹了使用dom4j解析xml文件,并轉(zhuǎn)出json格式問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09mybatis報錯元素內(nèi)容必須由格式正確的字符數(shù)據(jù)或標記組成異常的解決辦法
今天小編就為大家分享一篇關(guān)于mybatis查詢出錯解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12Java中的位運算符號解讀(&、|、^、~、<<、>>、>>>)
這篇文章主要介紹了Java中的位運算符號(&、|、^、~、<<、>>、>>>),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08Spring引入外部屬性文件配置數(shù)據(jù)庫連接的步驟詳解
這篇文章主要介紹了Spring引入外部屬性文件配置數(shù)據(jù)庫連接的步驟詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01