装饰器模式与java.io包
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
?????? Decorator設(shè)計(jì)模式是典型的結(jié)構(gòu)型模式(在GOF的那本模式的Bible中將模式分為:1.創(chuàng)建型模式;2.結(jié)構(gòu)型模式;3.行為模式三種)。它的主要用意是:動(dòng)態(tài)地為對(duì)象添加一些額外的功能。(記住上面兩種顏色的詞匯,理解裝飾器模式的精髓所在!)下面是GOF的《Element?of?reusable?Object-Oriented Software》中對(duì)Decorator用意的概述:
Decorator Pattern――Attaches additional responsibilities to an object dynamically . Decorators provide a flexible alternative to subclassing for extending functionality .
1?何時(shí)需要使用裝飾器模式
???????GOF的那本Bible中關(guān)于裝飾器模式列舉的是一個(gè)文本組件與邊框的例子(在這里我就不舉了,主要是因?yàn)槲視?huì)在書中舉一個(gè)相似的,但卻非常有說服力的例子,它對(duì)Swing中的某些本來應(yīng)該使用Decorator卻沒有使用的對(duì)象的改進(jìn)。同時(shí)會(huì)提出內(nèi)包裝、外包裝的概念。看到這個(gè)例子后大家仔細(xì)體會(huì)吧!通過例子告訴大家一點(diǎn):任何設(shè)計(jì)不是一成不變的、模式的應(yīng)用是極其靈活的……)。下面我舉一個(gè)“三明治”的例子!
???????很 多人都吃過三明治(我除外!“沒吃過豬肉,俺可聽過豬叫”),都會(huì)知道三明治必不可少的是兩塊面包片,然后可以在夾層里加上蔬菜、沙拉、咸肉等等,外面可 以涂上奶油之類的。假如現(xiàn)在你要為一個(gè)三明治小店構(gòu)造一個(gè)程序,其中要設(shè)計(jì)各種三明治的對(duì)象。可能你已經(jīng)創(chuàng)建了一個(gè)簡單的Sandwich對(duì)象,現(xiàn)在要產(chǎn)生帶蔬菜的就是繼承原有的Sandwich添加一個(gè)蔬菜的成員變量,看起來很“正點(diǎn)”的做法,以后我還要帶咸肉的、帶奶油的、帶蔬菜的又分為帶青菜的、帶芹菜的、生菜的……還是一個(gè)一個(gè)繼承是吧!假如我們還需要即帶蔬菜又帶其它肉類,設(shè)置我們還要求這些添加成分的任意組合,那你就慢慢繼承吧!
???????讀過幾年書的會(huì)下面這個(gè)算術(shù),我們有n種成分,在做三明治的時(shí)候任意搭配,那么有多少種方案呢?!算算吧!你會(huì)有驚人的發(fā)現(xiàn)。N種成分,什么都不要是Cn0種方案吧!要1種是Cn1吧!…..要n種是Cnn吧!加起來不就是嗎?Cn0+Cn1+……+Cnn-1+Cnn還不會(huì)啊!牛頓萊布尼茲公式記得吧!(可惜Word的公式編輯器安裝不了)總共2的n次方案。有可能前面10天寫了K個(gè)類,老板讓你再加一種成分你就得再干10天,下一次再加一種你可得干20天哦!同時(shí)你可以發(fā)現(xiàn)你的類庫急劇地膨脹!(老板可能會(huì)說你:XXX前K天你加了n個(gè)成分,怎么現(xiàn)在這么不上進(jìn)呢?后K天只加了1個(gè)成分啊?!!可能你會(huì)拿個(gè)比給老板算算,老板那么忙會(huì)睬你嗎?!有可能你的老板會(huì)說:不管怎么樣我就要你加,K天你還給我加n個(gè)成分!!呵呵,怎么辦啊!跳槽啊!跳槽了也沒人要你!!人家一看就知道你沒學(xué)設(shè)計(jì)模式)。下面我們就使用裝飾器模式來設(shè)計(jì)這個(gè)庫吧!下圖是我們的設(shè)計(jì)圖:
???????下面是以上各個(gè)類的意義:
1.Ingredient(成分):所有類的父類,包括它們共有的方法,一般為抽象類且方法都有默認(rèn)的實(shí)現(xiàn),也可以為接口。它有Bread和Decorator兩個(gè)子類。這種實(shí)際不存在的,系統(tǒng)需要的抽象類僅僅表示一個(gè)概念,圖中用紅色表示。
2. Bread(面包):就是我們?nèi)髦沃斜仨毜膬善姘K窍到y(tǒng)中最基本的元素,也是被裝飾的元素,和IO中的媒質(zhì)流(原始流)一個(gè)意義。在裝飾器模式中屬于一類角色,所以其顏色為紫色。
3. Decorator(裝飾器):所有其它成分的父類,這些成分可以是豬肉、羊肉、青菜、芹菜。這也是一個(gè)實(shí)際不存在的類,僅僅表示一個(gè)概念,即具有裝飾功能的所有對(duì)象的父類。圖中用藍(lán)色表示。
4.Pork(豬肉):具體的一個(gè)成分,不過它作為裝飾成分和面包搭配。
5. Mutton(羊肉):同上。
6. Celery(芹菜):同上。
7. Greengrocery(青菜):同上。
總結(jié)一下裝飾器模式中的四種角色:1.被裝飾對(duì)象(Bread);2.裝飾對(duì)象(四種);3.裝飾器(Decorator);4.公共接口或抽象類(Ingredient)。其中1和2是系統(tǒng)或者實(shí)際存在的,3和4是實(shí)現(xiàn)裝飾功能需要的抽象類。
???????寫段代碼體會(huì)其威力吧!(程序很簡單,但是實(shí)現(xiàn)的方法中可以假如如何你需要的方法,意境慢慢體會(huì)吧!)
//Ingredient.java public abstract class Ingredient {public abstract String getDescription();public abstract double getCost();public void printDescription(){System.out.println(" Name "+ this.getDescription());System.out.println(" Price RMB "+ this.getCost());} }???????所有成分的父類,抽象類有一個(gè)描述自己的方法和一個(gè)得到價(jià)格的方法,以及一個(gè)打印自身描述和價(jià)格的方法(該方法與上面兩個(gè)方法構(gòu)成模板方法哦!)
//Bread.java public class Bread extends Ingredient {private String description ;public Bread(String desc) {this.description=desc ;}public String getDescription() {return description ;}public double getCost() {return 2.48 ;} }???????面包類,因?yàn)樗且粋€(gè)具體的成分,因此實(shí)現(xiàn)父類的所有的抽象方法。描述可以通過構(gòu)造器傳入,也可以通過set方法傳入。同樣價(jià)格也是一樣的,我就很簡單地返回了。
//Decorator.java public abstract class Decorator extends Ingredient {Ingredient ingredient ;public Decorator(Ingredient igd) {this.ingredient = igd;}public abstract String getDescription();public abstract double getCost();}???????裝飾器對(duì)象,所有具體裝飾器對(duì)象父類。它最經(jīng)典的特征就是:1.必須有一個(gè)它自己的父類為自己的成員變量;2.必須繼承公共父類。這是因?yàn)檠b飾器也是一種成分,只不過是那些具體具有裝飾功能的成分的公共抽象罷了。在我們的例子中就是有一個(gè)Ingredient作為其成員變量。Decorator繼承了Ingredient類。
//Pork.java public class Pork extends Decorator {public Pork(Ingredient igd) {super(igd);}public String getDescription() {String base = ingredient.getDescription();return base +"\n"+"Decrocated with Pork !";}public double getCost() {double basePrice = ingredient.getCost();double porkPrice = 1.8;return basePrice + porkPrice ;} }???????具體的豬肉成分,同時(shí)也是一個(gè)具體的裝飾器,因此它繼承了Decorator類。豬肉裝飾器裝飾可以所有的其他對(duì)象,因此通過構(gòu)造器傳入一個(gè)Ingredient的實(shí)例,程序中調(diào)用了父類的構(gòu)造方法,主要父類實(shí)現(xiàn)了這樣的邏輯關(guān)系。同樣因?yàn)榉椒ㄊ蔷唧w的成分,所以getDescription得到了實(shí)現(xiàn),不過由于它是具有裝飾功能的成分,因此它的描述包含了被裝飾成分的描述和自身的描述。價(jià)格也是一樣的。價(jià)格放回的格式被裝飾成分與豬肉成分的種價(jià)格哦!
???????從上面兩個(gè)方法中我們可以看出,豬肉裝飾器的功能得到了增強(qiáng),它不僅僅有自己的描述和價(jià)格,還包含被裝飾成分的描述和價(jià)格。主要是因?yàn)楸谎b飾成分是它的成員變量,因此可以任意調(diào)用它們的方法,同時(shí)可以增加自己的額外的共同,這樣就增強(qiáng)了原來成分的功能。?????
//Mutton.java public class Mutton extends Decorator {public Mutton(Ingredient igd) {super(igd);}public String getDescription() {String base = ingredient.getDescription();return base +"\n"+"Decrocated with Mutton !";}public double getCost() {double basePrice = ingredient.getCost();double muttonPrice = 2.3;return basePrice + muttonPrice ;} }???????羊肉的包裝器。
//Celery.java public class Celery extends Decorator {public Celery(Ingredient igd) {super(igd);}public String getDescription() {String base = ingredient.getDescription();return base +"\n"+"Decrocated with Celery !";}public double getCost() {double basePrice = ingredient.getCost();double celeryPrice =0.6;return basePrice + celeryPrice ;} }???????芹菜的包裝器。
//GreenGrocery.java public class GreenGrocery extends Decorator {public GreenGrocery (Ingredient igd) {super(igd);}public String getDescription() {String base = ingredient.getDescription();return base +"\n"+"Decrocated with GreenGrocery !";}public double getCost() {double basePrice = ingredient.getCost();double greenGroceryPrice = 0.4;return basePrice + greenGroceryPrice;} }???????青菜的包裝器。?????
???????下面我們就領(lǐng)略裝飾器模式的神奇了!我們有一個(gè)測(cè)試類,其中建立夾羊肉的三明治、全蔬菜的三明治、全葷的三明治。(感覺感覺吧!很香的哦!)
public class DecoratorTest {public static void main(String[] args) {Ingredient compound = new Mutton(new Celery(new Bread("Master24's Bread")));compound.printDescription();compound = new Celery(new GreenGrocery(new Bread("Bread with milk")));compound.printDescription();compound = new Mutton(new Pork(new Bread("Bread with cheese")));compound.printDescription();} }???????以上就是一個(gè)簡單的裝飾器類!假如你對(duì)想中國式的吃法,可以將加入饅頭、春卷皮、蛋皮……夾菜可以為肉絲……突然想到了京醬肉絲。?????
2?裝飾器模式的結(jié)構(gòu)
???????在談及軟件中的結(jié)構(gòu),一般會(huì)用UML圖表示(UML和ANT、JUnit等都是軟件設(shè)計(jì)中基本的工具,會(huì)了沒有啊!)。下面是一個(gè)我們經(jīng)常看到的關(guān)于Decorator模式的結(jié)構(gòu)圖。
1.Component就是裝飾器模式中公共方法的類,在裝飾器模式結(jié)構(gòu)圖的頂層。
2.ConcreateComponent是轉(zhuǎn)換器模式中具體的被裝飾的類,IO包中的媒體流就是此種對(duì)象。
3.Decorator裝飾器模式中的核心對(duì)象,所有具體裝飾器對(duì)象的父類,完成裝飾器的部分職能。在上面的例子中Decorator類和這里的對(duì)應(yīng)。該類可以只做一些簡單的包裹被裝飾的對(duì)象,也可以還包含對(duì)Component中方法的實(shí)現(xiàn)……他有一個(gè)鮮明的特點(diǎn):繼承至Component,同時(shí)包含一個(gè)Component作為其成員變量。裝飾器模式動(dòng)機(jī)中的動(dòng)態(tài)地增加功能是在這里實(shí)現(xiàn)的。
4.ConcreteDecoratorA和ConcreteDecoratorB是兩個(gè)具體的裝飾器對(duì)象,他們完成具體的裝飾功能。裝飾功能的實(shí)現(xiàn)是通過調(diào)用被裝飾對(duì)象對(duì)應(yīng)的方法,加上裝飾對(duì)象自身的方法。這是裝飾器模式動(dòng)機(jī)中的添加額外功能的關(guān)鍵。
從上面圖中你可能還會(huì)發(fā)現(xiàn):ConcreteDecoratorA和ConcreteDecoratorB的方法不一樣,這就是一般設(shè)計(jì)模式中談及裝飾器模式的“透明裝飾器”和“不透明裝飾器”。“透明裝飾器”就是整個(gè)Decorator的結(jié)構(gòu)中所有的類都保持同樣的“接口”(這里是共同方法的意思),這是一種極其理想的狀況,就像餐飲的例子一樣。現(xiàn)實(shí)中絕大多數(shù)裝飾器都是“不透明裝飾器”,他們的“接口”在某些子類中得到增強(qiáng),主要看這個(gè)類與頂層的抽象類或者接口是否有同樣的公共方法。IO中的ByteArrayInputStream就比Inputstrem抽象類多一些方法,因此IO中的裝飾器是一個(gè)“不通明裝飾器”。下面是IO中輸入字節(jié)流部分的裝飾器的結(jié)構(gòu)圖。
1. InputStream是裝飾器的頂層類,一個(gè)抽象類!包括一些共有的方法,如:1.讀方法――read(3個(gè));2.關(guān)閉流的方法――close;3.mark相關(guān)的方法――mark、reset和markSupport;4.跳躍方法――skip;5.查詢是否還有元素方法――available。圖中紅色的表示。
2.FileInputStream、PipedInputStream…五個(gè)紫色的,是具體的被裝飾對(duì)象。從他們的“接口”中可以看出他們一般都有額外的方法。
3. FilterInputStream是裝飾器中的核心,Decorator對(duì)象,圖中藍(lán)色的部分。
4. DataInputStream、BufferedInputStream…四個(gè)是具體的裝飾器,他們保持了和InputStream同樣的接口。
5.? ObjectInputStream是IO字節(jié)輸入流中特殊的裝飾器,他不是FilterInputStream的子類(不知道Sun處于何種意圖不作為FileterInputStream的子類,其中流中也有不少的例子)。他和其他FilterInputStream的子類功能相似都可以裝飾其他對(duì)象。?
IO包中不僅輸入字節(jié)流是采用裝飾器模式、輸出字節(jié)流、輸入字符流和輸出字符流都是采用裝飾器模式。關(guān)于IO中裝飾器模式的實(shí)現(xiàn)可以通過下面的源代碼分析從而了解細(xì)節(jié)。
3 裝飾器模式與適配器模式的比較
? ? ?共同點(diǎn):都擁有一個(gè)目標(biāo)對(duì)象。裝飾器通過包裝一個(gè)裝飾對(duì)象來擴(kuò)展其功能,而又不改變其接口,這實(shí)際上是基于對(duì)象的適配器模式的一種變種。
? ? ?不同點(diǎn):適配器模式需要實(shí)現(xiàn)另外一個(gè)接口,而裝飾器模式必須實(shí)現(xiàn)該對(duì)象的接口。適配器模式主要是為了接口的轉(zhuǎn)換,而裝飾者模式關(guān)注的是通過組合來動(dòng)態(tài)的為被裝飾者注入新的功能或行為(即所謂的責(zé)任)。?
轉(zhuǎn)載于:https://my.oschina.net/fuyong/blog/727358
總結(jié)
以上是生活随笔為你收集整理的装饰器模式与java.io包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VII Python(9)socket编
- 下一篇: Composer 中国全量镜像(二)