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

歡迎訪問 生活随笔!

生活随笔

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

asp.net

设计模式--装饰模式

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

目錄

什么是裝飾模式?

應用代碼示例

裝飾模式模板

jdk中的裝飾模式

為什么裝飾器類不能直接實現Component父類?


什么是裝飾模式?

以生活中的場景來舉例,一個蛋糕胚,給它涂上奶油就變成了奶油蛋糕,再加上巧克力和草莓,它就變成了巧克力草莓蛋糕。

像這樣能夠不斷地向對象添加裝飾的設計模式就稱為裝飾模式(Decorator模式)。

這里添加的東西就是裝飾器,被添加的對象就是被裝飾對象,在蛋糕的場景中,蛋糕是被裝飾的對象,裝飾器就是奶油,巧克力這些。一般來說裝飾模式會滿足is a的關系,被裝飾對象如果怎么添加裝飾,其本質不變,就像蛋糕加再多東西,其本質都是蛋糕。

應用代碼示例

假設我們需要設計一個獎金的系統,目前一個工作人員的獎金包含三部分:

本月銷售額的獎金 : 當月銷售額的3%

累計銷售額的獎金 : 累計銷售額的0.1%

團隊銷售額的獎金 : 團隊銷售額的1%,只有經理才有

使用裝飾模式的實現如下 : 定義一個所有獎金的抽象接口,然后定義一個BasicPrize的初始獎金對象,不同的人獎金的組成不同,就為其添加不同的裝飾器?

/*** @Description* @Author chenpp* @Date 2019/10/13 19:27*/ public abstract class Component {abstract double calPrize(String userName); } /*** @Description* @Author chenpp* @Date 2019/10/13 19:28* 默認的基礎對象: 獎金--0*/ public class BasicPrize extends Component {//保存了一個員工與銷售額的對應關系的mappublic static Map<String,Double> saleMoney = new HashMap<String,Double>();static {saleMoney.put("小明",9000.0);saleMoney.put("小陳",20000.0);saleMoney.put("小王",30000.0);saleMoney.put("張經理",55000.0);}public double calPrize(String userName) {return 0;} } /*** @Description* @Author chenpp* @Date 2019/10/13 19:28* 所有裝飾器的抽象父類,持有一個被裝飾對象*/ public abstract class Decorator extends Component {protected Component component;public Decorator(Component component){this.component = component;}public abstract double calPrize(String userName); } /*** @Description* @Author chenpp* @Date 2019/10/13 19:48* 當月獎金計算規則*/ public class MonthPrizeDecorator extends Decorator{public MonthPrizeDecorator(Component component) {super(component);}public double calPrize(String userName) {//先計算被裝飾對象的獎金(就是在加上本獎金之前的獎金)double money = this.component.calPrize(userName);//計算本獎金 對應員工的業務額的3%double prize = BasicPrize.saleMoney.get(userName)*0.03;System.out.println(userName+"當月 業務獎金:"+prize);return money + prize;}} /*** @Description* @Author chenpp* @Date 2019/10/13 19:33* 累計獎金*/ public class SumPrizeDecorator extends Decorator {public SumPrizeDecorator(Component component) {super(component);}public double calPrize(String userName) {//先計算被裝飾對象的獎金(就是在加上本獎金之前的獎金)double money = this.component.calPrize(userName);//計算本獎金 累計業務額的0.1% 假設是100000double prize = 100000*0.001;System.out.println(userName+"當月 累計獎金:"+prize);return money + prize;}} /*** @Description* @Author chenpp* @Date 2019/10/13 19:34* 團隊獎金*/ public class GroupPrizeDecorator extends Decorator {public GroupPrizeDecorator(Component component) {super(component);}public double calPrize(String userName) {//先計算被裝飾對象的獎金(就是在加上本獎金之前的獎金)double money = this.component.calPrize(userName);//計算本獎金 本團隊的業務額的1%double sumSale = 0;for(double sale: BasicPrize.saleMoney.values()){sumSale += sale;}double prize = sumSale*0.01;System.out.println(userName+"當月 團隊獎金:"+prize);return money + prize;}} /*** @Description* @Author chenpp* @Date 2019/10/13 20:01*/ public class Client {public static void main(String[] args) throws FileNotFoundException {//基礎對象 獎金0Component basePrice = new BasicPrize();//當月獎金Component salePrize = new MonthPrizeDecorator(basePrice);//累計獎金Component sumPrize = new SumPrizeDecorator(salePrize);//團隊獎金Component groupPrize = new GroupPrizeDecorator(sumPrize);//普通員工的獎金由兩部分組成double d1 = sumPrize.calPrize("小明");System.out.println("========================小明總獎金:"+d1);double d2 = sumPrize.calPrize("小陳");System.out.println("========================小陳總獎金:"+d2);double d3 = sumPrize.calPrize("小王");System.out.println("========================小王總獎金:"+d3);//王經理的獎金由三部分組成double d4 = groupPrize.calPrize("張經理");System.out.println("========================張經理總獎金:"+d4);} }

輸出結果:

這里我們使用裝飾器模式,主要是為了方便組合和復用。在一個繼承的體系中,子類往往是互斥的,比方在一個奶茶店,它會有絲襪奶茶,紅茶,果茶等,用戶想要一杯飲料,一般都會在這些種類中選一種,不能一杯飲料既是果茶又是奶茶。然后用戶可以根據自己的喜好添加任何想要的decorators,珍珠,椰果,布丁等,這些添加物對所有茶類飲品都是相互兼容的,并且是可以被允許反復添加的(同樣的裝飾器是否允許在同一個對象上裝飾多次,視情況而定,像上面的獎金場景顯然是不被允許的)

裝飾模式模板

public abstract class Component {abstract void operation(); } /*** @Description* @Author chenpp* @Date 2019/10/13 19:28* 具體的被裝飾類*/ public class ConcreteComponent extends Component {public void operation() {} } /*** @Description* @Author chenpp* @Date 2019/10/13 19:28* 所有裝飾器的抽象父類,持有一個被裝飾對象*/ public class Decorator extends Component {protected Component component;public Decorator(Component component){this.component = component;}public void operation() {component.operation();} }

其角色可以分為三類:
Component?: 組件對象的接口,可以給這些對象動態的添加功能
ConcreteComponent:具體的組件對象,實現了Component接口,通常就是被裝飾器裝飾的原始對象
Decorator:所有裝飾器的抽象父類,需要定義與組件接口一致的接口,并且持有一個被裝飾的Component對象

Decorator和ConcreteComponent的父類接口都是Component,所以能夠保證在裝飾后還擁有裝飾前的特性,保證裝飾器和被裝飾物的一致性

其類圖如下:

ConcreteDecoratorA和ConcreteDecoratorB是具體的裝飾器對象,可以為原始對象添加不同的功能。理論上各裝飾器之間的功能最好完全獨立,互不依賴,這樣用戶在組合的時候才可以不受先后順序的限制,更加靈活且安全。

jdk中的裝飾模式

java.io包是用于輸入輸出的包,這里使用了大量的裝飾器模式

以輸出流為例,我們可以使用FileOutputStream fos = new FileOutputStream("file.txt");進行輸出,也可以添加各種裝飾器?

eg.BufferedOutputStream,DataOutputStream 等

下圖是jdk 輸出流的一部分類圖,很明顯是一個裝飾模式的類圖,OutputStream是頂層父類,FileOutputStream和ObjectOutputStream是具體的被裝飾類,FilterOutputStream是所有輸出流裝飾器的抽象父類

?

按照裝飾器模式的結構,我們可以繼承FilterOutputStream實現自定義的裝飾器,并且在使用的時候可以和jdk自帶的裝飾器對象任意組合。

這里實現了一個簡單的復制輸出內容的OutputStream裝飾器

public class DuplicateOutputStream2 extends FilterOutputStream {/*** Creates an output stream filter built on top of the specified* underlying output stream.** @param out the underlying output stream to be assigned to* the field <tt>this.out</tt> for later use, or* <code>null</code> if this instance is to be* created without an underlying stream.*/public DuplicateOutputStream2(OutputStream out) {super(out);}//將所有的內容復制一份輸出 ab 變成aabbpublic void write(int b) throws IOException {super.write(b);super.write(b);} }

?

為什么裝飾器類不能直接實現Component父類?

以輸出流為例,如果直接繼承OutputStream來實現自定義裝飾器

public class DuplicateOutputStream extends OutputStream {private OutputStream os;public DuplicateOutputStream(OutputStream os){this.os = os;}//將所有的內容復制一份輸出 ab 變成aabbpublic void write(int b) throws IOException {os.write(b);os.write(b);} } public class ClientTest {public static void main(String[] args) throws IOException {DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new DuplicateOutputStream2(new FileOutputStream("1.txt"))));testOutputStream(dataOutputStream);DataOutputStream dataOutputStream1 = new DataOutputStream(new BufferedOutputStream(new DuplicateOutputStream(new FileOutputStream("1.txt"))));testOutputStream(dataOutputStream1);}public static void testOutputStream(DataOutputStream dataOutputStream) throws IOException {DataInputStream dataInputStream = new DataInputStream(new FileInputStream("1.txt"));dataOutputStream.write("bdsaq".getBytes());dataOutputStream.close();System.out.println(dataInputStream.available());byte[] bytes3 = new byte[dataInputStream.available()];dataInputStream.read(bytes3);System.out.println("文件內容:"+new String(bytes3));} }

輸出結果:

乍一看好像沒什么區別,但是如果把BufferedOutputStream裝飾器和自定義的裝飾器互換。

DataOutputStream dataOutputStream2 = new DataOutputStream(new DuplicateOutputStream2(new BufferedOutputStream(new FileOutputStream("1.txt")))); testOutputStream(dataOutputStream2);DataOutputStream dataOutputStream3 = new DataOutputStream(new DuplicateOutputStream(new BufferedOutputStream(new FileOutputStream("1.txt")))); testOutputStream(dataOutputStream3);

輸出結果:

使用了實現OutputStream的DuplicateOutputStream會出現沒有正常輸出數據,這是因為我們使用了BufferedOutputStream這個帶緩存區的輸出流,緩存區的輸出流在緩存區沒有滿的情形下是不會進行輸出操作的。一般情形下我們在調用jdk的DataOutputStream的close方法的時候會調用其傳入的輸出流的flush()方法,并且向下傳遞調用,BufferedOutputStream里的數據會正常輸出。

在使用DuplicateOutputStream2的時候其調用關系是這樣的:

dataOutputStream.close()-->duplicateOutputStream2.flush()-->bufferedOutputStream.flush()

使用DuplicateOutputStream的時候由于DuplicateOutputStream繼承的OutputStream的flush()方法是空實現,所以不會繼續往下調用bufferedOutputStream的flush()方法,故而最后沒有得到輸出內容

所以裝飾器類需要繼承Decorator抽象父類,而不是直接繼承Component抽象類,我認為是為了在Decorator里實現一些共性的代碼,以便在使用裝飾器的時候能夠更加自由,無視其組合順序


參考資料:

<<研磨設計模式>>

總結

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

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