日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

设计模式之装饰者模式

發布時間:2023/12/16 asp.net 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设计模式之装饰者模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

定義

在不改變原有對象的基礎上,將功能附加到原有功能上,進行功能的擴展,
動態地給一個對象增加一些額外的功能,裝飾模式比生成子類(繼承)實現更為靈活。
在裝飾者模式中,為了讓系統具有更好的靈活性和可擴展性,我們通常會定義一個抽象裝飾類,而將具體的裝飾類作為它的子類

類型

結構型

UML圖

裝飾者(Decorator)和具體組件(ConcreteComponent)都繼承自組件(Component),具體組件的方法實現不需要依賴于其它對象,而裝飾者組合了一個組件,這樣它可以裝飾其它裝飾者或者具體組件。所謂裝飾,就是把這個裝飾者套在被裝飾者之上,從而動態擴展被裝飾者的功能。裝飾者的方法有一部分是自己的,這屬于它的功能,然后調用被裝飾者的方法實現,從而也保留了被裝飾者的功能。可以看到,具體組件應當是裝飾層次的最低層,因為只有具體組件的方法實現不需要依賴于其它對象。

角色

Component(抽象組件):它是具體組件和抽象裝飾類的共同父類,聲明了在具體組件中實現的業務方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對象以及裝飾之后的對象,實現客戶端的透明操作。

ConcreteComponent(具體組件):它是抽象組件類的子類,用于定義具體的組件對象,實現了在抽象組件中聲明的方法,裝飾器可以給它增加額外的職責(方法)。

Decorator(抽象裝飾類):它也是抽象組件類的子類,用于給具體組件增加職責,但是具體職責在其子類中實現。它維護一個指向抽象組件對象的引用,通過該引用可以調用裝飾之前組件對象的方法,并通過其子類擴展該方法,以達到裝飾的目的。 對于Component(抽象組件)來說,是無需了解Decorator(抽象裝飾類)的存在的,Component(抽象組件)只顧做好自己份內的事就行。

ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負責向組件添加新的職責。每一個具體裝飾類都定義了一些新的行為,它可以調用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴充對象的行為。

由于具體組件類和裝飾類都實現了相同的抽象組件接口,因此裝飾模式以對客戶透明的方式動態地給一個對象附加上更多的責任,換言之,客戶端并不會覺得對象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要創造更多子類的情況下,將對象的功能加以擴展。

裝飾模式的核心在于抽象裝飾類的設計。

示例:

version1:通過繼承給一個類添加功能

煎餅類(實現基本的功能)

public class Battercake {protected String getDesc(){return "煎餅";}protected int cost(){return 8;}}

加雞蛋的煎餅類(通過繼承給煎餅類添加雞蛋)

public class BattercakeWithEgg extends Battercake {@Overridepublic String getDesc() {return super.getDesc()+" 加一個雞蛋";}@Overridepublic int cost() {return super.cost()+1;}}

加雞蛋和香腸的煎餅類(使用繼承繼續添加新功能)

public class BattercakeWithEggSausage extends BattercakeWithEgg {@Overridepublic String getDesc() {return super.getDesc()+ " 加一根香腸";}@Overridepublic int cost() {return super.cost()+2;} }

Test類

public class Test {public static void main(String[] args) {Battercake battercake = new Battercake();System.out.println(battercake.getDesc()+" 銷售價格:"+battercake.cost());Battercake battercakeWithEgg = new BattercakeWithEgg();System.out.println(battercakeWithEgg.getDesc()+" 銷售價格:"+battercakeWithEgg.cost());Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();System.out.println(battercakeWithEggSausage.getDesc()+" 銷售價格:"+battercakeWithEggSausage.cost());} }

version2:通過裝飾者模式

抽象組件(抽象煎餅類)

public abstract class ABattercake {protected abstract String getDesc();protected abstract int cost();}

具體組件(煎餅類)

public class Battercake extends ABattercake {@Overrideprotected String getDesc() {return "煎餅";}@Overrideprotected int cost() {return 8;} }

抽象裝飾類,抽象裝飾類通過成員屬性的方式將煎餅抽象類組合進來,同時也繼承了煎餅抽象類,且這里添加了新功能 doSomething()

public abstract class AbstractDecorator extends ABattercake {private ABattercake aBattercake;public AbstractDecorator(ABattercake aBattercake) {this.aBattercake = aBattercake;}protected abstract void doSomething();@Overrideprotected String getDesc() {return this.aBattercake.getDesc();}@Overrideprotected int cost() {return this.aBattercake.cost();} }

雞蛋裝飾器,繼承了抽象裝飾類,雞蛋裝飾器在父類的基礎上增加了一個雞蛋,同時價格加上 1 塊錢

public class EggDecorator extends AbstractDecorator {public EggDecorator(ABattercake aBattercake) {super(aBattercake);}@Overrideprotected void doSomething() {System.out.println("準備好,我要添加雞蛋了");}@Overrideprotected String getDesc() {return super.getDesc()+" 加一個雞蛋";}@Overrideprotected int cost() {return super.cost()+1;}protected void dance(){System.out.println("看我一邊跳舞,一邊加雞蛋");}}

香腸裝飾器,與雞蛋裝飾器類似,繼承了抽象裝飾類,給在父類的基礎上加上一根香腸,同時價格增加 2 塊錢

public class SausageDecorator extends AbstractDecorator {public SausageDecorator(ABattercake aBattercake) {super(aBattercake);}@Overrideprotected void doSomething() {System.out.println("準備好了沒有,我要開始加香腸了");}@Overrideprotected String getDesc() {return super.getDesc()+" 加一根香腸";}@Overrideprotected int cost() {return super.cost()+2;}protected void sing(){System.out.println("are you ok? 我要開始加香腸了,烤面筋啊我的烤面筋,搞錯了,再來");} }

測試Test

public class Test {public static void main(String[] args) {ABattercake aBattercake;aBattercake = new Battercake();System.out.println(aBattercake.getDesc()+" 銷售價格:"+aBattercake.cost());aBattercake = new EggDecorator(aBattercake);System.out.println(aBattercake.getDesc()+" 銷售價格:"+aBattercake.cost());// System.out.println(aBattercake.doSomething()); // System.out.println(aBattercake.dance());aBattercake = new SausageDecorator(aBattercake);System.out.println(aBattercake.getDesc()+" 銷售價格:"+aBattercake.cost());//半透明裝飾模式EggDecorator eggDecorator = new EggDecorator(aBattercake);eggDecorator.doSomething();eggDecorator.dance();SausageDecorator sausageDecorator = new SausageDecorator(aBattercake);sausageDecorator.doSomething();sausageDecorator.sing();// AbstractDecorator abstractDecorator = new EggDecorator(aBattercake); // abstractDecorator.doSomething(); // abstractDecorator.dance();} }

煎餅類對象還是那個煎餅類對象,但是會有裝飾類對象來給他增加新東西,并且各個裝飾類對象的裝飾并沒有先后關系,都是直接裝飾原始煎餅類對象。多個裝飾如果有先后依賴關系(比如存儲數據之前,做一下數據的過濾和加密。如果先加密了數據,那就不好過濾數據了,要先過濾數據,再對數據進行加密),那就要注意裝飾順序了。不過最好將裝飾的東西之間設計成獨立的,沒有先后依賴關系,能以任何順序進行組合。

透明裝飾模式和半透明裝飾模式

1.透明裝飾模式

在透明裝飾模式中要求客戶端完全針對抽象編程,裝飾模式的透明性要求客戶端程序不應該將對象聲明為具體組件類型或具體裝飾類型,而應該全部聲明為抽象組件類型。對客戶端而言,具體組件類和具體裝飾類對象沒有任何區別。(缺點,無法單獨調用裝飾類的獨有功能)

上面的例子就是透明裝飾模式,裝飾后的對象都是通過抽象組間類類型 ABattercake 的變量來引用的,然后通過組合方式注入到其他具體裝飾對象實例里面,但是這樣的話就只能調用抽象組間類里面已經定義的方法(具體裝飾類針對這些定義的方法做了一些裝飾)

ABattercake aBattercake; aBattercake = new EggDecorator(aBattercake);

2.半透明裝飾模式

用具體裝飾類型來定義裝飾后的對象,而具體組件類型仍然可以使用抽象組件類型來定義,可以單獨調用裝飾的獨有方法。(缺點:無法多次進行裝飾)

透明裝飾模式的設計難度較大,而且有時我們需要單獨調用新增的業務方法。為了能夠調用到新增方法,我們不得不用具體裝飾類型來定義裝飾之后的對象,而具體組件類型還是可以使用抽象組件類型來定義,這種裝飾模式即為半透明裝飾模式。

半透明裝飾模式可以給系統帶來更多的靈活性,設計相對簡單,使用起來也非常方便;但是其最大的缺點在于不能實現對同一個對象的多次裝飾,而且客戶端需要有區別地對待裝飾之前的對象和裝飾之后的對象。

ABattercake aBattercake; EggDecorator eggDecorator = new EggDecorator(aBattercake); eggDecorator.doSomething(); eggDecorator.dance();SausageDecorator sausageDecorator = new SausageDecorator(aBattercake); sausageDecorator.doSomething(); sausageDecorator.sing();

裝飾者模式總結

優點

  • 通過使用不同裝飾類以及這些類的排列組合,可以實現不同的效果。
  • 裝飾者模式和繼承關系的目的都是要擴展原有對象的功能,但是裝飾者模式可以提供比繼承更多的靈活型,不會導致類的個數急劇增加
  • 類應該對擴展開放,對修改關閉(符合開閉原則): 具體組件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體組件類和具體裝飾類,原有類庫代碼無須改變。
  • 具體組件類的職責更加清晰簡潔,有效的把類的核心職責和裝飾功能區分開,不會產生很多冗余的功能,簡化了原有的類,只有在需要某功能的時候,才由裝飾者裝飾添加功能。并且這樣也能去除相關類中重復的裝飾邏輯

缺點

  • 更多的代碼,更多的類,增加程序的復雜性。
  • 動態裝飾時、多層裝飾時會使系統更復雜。
  • 越靈活也意味著更容易出錯。裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易于出錯,排錯也更困難,對于多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較為繁瑣
  • 使用裝飾模式進行系統設計時將產生很多小對象(大對象包小對象,通過組合的形式將小對象注入到大對象里面),大量小對象的產生勢必會占用更多的系統資源,在一定程序上影響程序的性能。

適用場景

  • 擴展一個類的功能或者給一個類添加附加職責
  • 在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責
  • 需要動態地給一個對象增加功能,這些功能也可以動態地被撤銷
  • 當不能采用繼承的方式對系統進行擴展或者采用繼承不利于系統擴展和維護時可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統中存在大量獨立的擴展,為支持每一種擴展或者擴展之間的組合將產生大量的子類,使得子類數目呈爆炸性增長;第二類是因為類已定義為不能被繼承(如Java語言中的final類)。

相關設計模式比較

  • 裝飾者模式和代理模式: 裝飾者模式關注的是對象功能的動態添加。而代理模式關注的是對對象的控制訪問,對它的用戶隱藏對象的具體信息。
  • 裝飾者模式和適配器模式:裝飾者模式和被裝飾的類要實現同一個接口,或者裝飾類是被裝飾的類的子類。 適配器模式和被適配的類具有不同的接口。

應用:

Java I/O中的裝飾者模式


由上圖可知在Java中應用程序通過輸入流(InputStream)的Read方法從源地址處讀取字節,然后通過輸出流(OutputStream)的Write方法將流寫入到目的地址。流的來源主要有三種:
①本地的文件(File)
②控制臺輸入
③通過socket實現的網絡通信

我們可以通過其他博主的圖片看一看java IO的裝飾者模式,
下面的圖可以看出Java中的裝飾者類和被裝飾者類以及它們之間的關系,這里只列出了InputStream中的關系:

由上圖可以看出只要繼承了FilterInputStream的類就是裝飾者類,可以用于包裝其他的流,裝飾者類還可以對裝飾者和類進行再包裝

如下代碼是使用的半透明裝飾模式

public class StreanDemo {public static void main(String[] args) throws IOException {DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\JAVAworkspace\\ProgramTest\\src\\StreamDemo.java")));while(in.available()!=0){System.out.print((char)in.readByte());}in.close();}}

上面程序中對流進行了兩次包裝,先用 BufferedInputStream將FileInputStream包裝成緩沖流也就是給FileInputStream增加緩沖功能,再DataInputStream進一步包裝方便數據處理。

自定義裝飾類
同理,你可以繼承抽象裝飾類,來實現自己定義的具體裝飾類
如下代碼:將輸入流中的所有大寫字母變成小寫字母

public class LowerCaseInputStream extends FilterInputStream {protected LowerCaseInputStream(InputStream in) {super(in);}@Overridepublic int read() throws IOException {int c=super.read();return (c==-1?c:Character.toLowerCase(c));}@Overridepublic int read(byte[] b, int off, int len) throws IOException {int result=super.read(b, off, len);for(int i=off;i<off+result;i++){b[i]=(byte)Character.toUpperCase((char)b[i]);}return result;}public static void main(String[] args) throws IOException {int c;try (InputStream in = new LowerCaseInputStream(new FileInputStream(文件路徑))) {try {while ((c = in.read()) >= 0) {System.out.print((char) c);}} finally {in.close();}}}}

結果展示

幾種常用流的應用場景:

流名稱應用場景
ByteArrayInputStream訪問數組,把內存中的一個緩沖區作為 InputStream 使用,CPU從緩存區讀取數據比從存儲介質的速率快10倍以上
StringBufferInputStream把一個 String 對象作為。InputStream。不建議使用,在轉換字符的問題上有缺陷
FileInputStream訪問文件,把一個文件作為 InputStream ,實現對文件的讀取操作
PipedInputStream訪問管道,主要在線程中使用,一個線程通過管道輸出流發送數據,而另一個線程通過管道輸入流讀取數據,這樣可實現兩個線程間的通訊
SequenceInputStream把多個 InputStream 合并為一個 InputStream . “序列輸入流”類允許應用程序把幾個輸入流連續地合并起來
DataInputStream特殊流,讀各種基本類型數據,如byte、int、String的功能
ObjectInputStream對象流,讀對象的功能
PushBackInputStream推回輸入流,可以把讀取進來的某些數據重新回退到輸入流的緩沖區之中
BufferedInputStream緩沖流,增加了緩沖功能

References:

  • https://coding.imooc.com/class/270.html
  • https://www.pdai.tech/md/dev-spec/pattern/12_decorator.html
  • https://whirlys.blog.csdn.net/article/details/82764333
  • https://blog.csdn.net/u013309870/article/details/75735676
  • 《大話設計模式》
  • https://blog.csdn.net/qq_37960603/article/details/104087989
  • https://blog.csdn.net/weixin_44045828/article/details/110490324

(寫博客主要是對自己學習的歸納整理,資料大部分來源于書籍和網絡資料,整理不易,但是難免有不足之處,如有錯誤,請大家評論區批評指正。同時感謝廣大博主和廣大作者辛苦整理出來的資源。)

總結

以上是生活随笔為你收集整理的设计模式之装饰者模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。