設計模式--裝飾模式

裝飾模式【Decorator Pattern】
Ladies and gentlemen,May I get your attention,Please?,Now I’m going to talk
about decorator pattern.裝飾模式在中國使用的那實在是多,中國的文化是中庸文化,說
話或做事情都不能太直接,需要有技巧的,比如說話吧,你要批評一個人,你不能一上來就
說你這個做的不對,那個做的不對,你要先肯定他的成績,表揚一下優點,然後再指出瑕疵,
指出錯誤的地方,最後再來個激勵,你修改了這些缺點後有那些好處,比如你能帶更多的小
兵,到個小頭目等等,否則你一上來就是一頓批評,你瞅瞅看,肯定是不服氣,頂撞甚至是
直接“此處不養爺,自有養爺處”開溜哇。這是說話,那做事情也有很多,在山寨產品流行
之前,假貨很是比較盛行的,我在2002 年買了個手機,當時老闆吹的是天花亂墜,承諾這
個手機是最新的,我看着也像,殼子是嶄新的,包裝是嶄新的,沒有任何瑕疵,就是比正品
便宜了一大截,然後我買了,缺錢哪,用來3 個月,壞了,一送修,檢查,說這是個新殼裝
舊機,我暈!拿一箇舊手機的線路板,找個新的外殼、屏幕、包裝就成了新手機,裝飾模式
害人不淺呀!
我們不說不開心的事情,今天舉一個什麼例子呢?就說說我上小學的的糗事吧。我上小
學的時候學習成績非常的差,班級上40 多個同學,我基本上都是在排名45 名以後,按照老
師給我的定義就是“不是讀書的料”,但是我老爸管的很嚴格,明知道我不是這塊料,還是
往趕鴨子上架,每次考試完畢我都是戰戰兢兢的,“竹筍炒肉”是肯定少不了的,能少點就
少點吧,肉可是自己的呀。四年級期末考試考完,學校出來個很損的招兒(這招兒現在很流
行的),打印出成績單,要家長簽字,然後才能上五年級,我那個恐懼呀,不過也就是幾秒
鐘的時間,玩起來什麼都忘記了。
我們先看看這個成績單的類圖:
您的設計模式
第 130 頁
成績單的抽象類,然後有一個四年級的成績單實現類,先看抽象類:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 成績單的抽象類
*/
public abstract class SchoolReport {
//成績單的主要展示的就是你的成績情況
public abstract void report();
//成績單要家長簽字,這個是最要命的
public abstract void sign();
}
然後看我們的實現類FouthGradSchoolReport:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
您的設計模式
第 131 頁
* 四年級的成績單,這個是我們學校第一次實施,以前沒有幹過
* 這種“缺德”事。
*/
public class FouthGradeSchoolReport extends SchoolReport {
//我的成績單
public void report() {
//成績單的格式是這個樣子的
System.out.println("尊敬的XXX家長:");
System.out.println(" ......");
System.out.println(" 語文 62 數學65 體育 98 自然 63");
System.out.println(" .......");
System.out.println(" 家長簽名: ");
}
//家長簽名
public void sign(String name) {
System.out.println("家長簽名爲:"+name);
}
}
成績單出來,你別看什麼62,65 之類的成績,你要知道在小學低於90 分基本上就是中
下等了,唉,愛學習的人太多了!怎麼着,那我把這個成績單給老爸看看?好,我們修改一
下類圖,成績單給老爸看:
老爸開始看成績單,這個成績單可是最真實的,啥都沒有動過,原裝,看Father 類:
您的設計模式
第 132 頁
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 老爸看成績單了
*/
public class Father {
public static void main(String[] args) {
//成績單拿過來
SchoolReport sr = new FouthGradeSchoolReport();
//看成績單
sr.report();
//簽名?休想!
}
}
運行結果如下:
尊敬的XXX家長:
......
語文 62 數學65 體育 98 自然 63
.......
家長簽名:
就這成績還要我簽字?!老爸就開始找笤帚,我的屁股已經做好了準備,肌肉要繃緊,要
不那個太疼了!哈哈,幸運的是,這個不是當時的真實情況,我沒有直接把成績單交給老爸,而
是在交給他之前做了點技術工作,我要把成績單封裝一下,封裝分類兩步走:
第一步:跟老爸說各個科目的最高分,語文最高是75,數學是78,自然是80,然老爸覺
的我成績與最高分數相差不多,這個是實情,但是不知道是什麼原因,反正期末考試都考的不怎
麼樣,但是基本上都集中在70 分以上,我這60 多分基本上還是墊底的角色;
第二步:在老爸看成績單後,告訴他我是排名第38 名,全班,這個也是實情,爲啥呢?有
將近十個同學退學了!這個情況我是不說的。不知道是不是當時第一次發成績單,學校沒有考慮
清楚,沒有寫上總共有多少同學,排名第幾名等等,反正是被我鑽了個空子。
您的設計模式
第 133 頁
那修飾是說完了,我們看看類圖如何修改:
我想這是你最容易想到的類圖,通過直接增加了一個子類,重寫report 方法,很容易的
解決了這個問題,是不是這樣?是的,確實是,確實是一個很好的辦法,我們來看具體的實現:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 對這個成績單進行美化
* Sugar這個詞太好了,名詞是糖的意思,動詞就是美化
* 給你顆糖你還不美去
*/
public class SugarFouthGradeSchoolReport extends
FouthGradeSchoolReport {
//首先要定義你要美化的方法,先給老爸說學校最高成績
private void reportHighScore(){
System.out.println("這次考試語文最高是75,數學是78,自然是80");
}
//在老爸看完畢成績單後,我再彙報學校的排名情況
private void reportSort(){
System.out.println("我是排名第38名...");
}
您的設計模式
第 134 頁
//由於彙報的內容已經發生變更,那所以要重寫父類
@Override
public void report(){
this.reportHighScore(); //先說最高成績
super.report(); //然後老爸看成績單
this.reportSort(); //然後告訴老爸學習學校排名
}
}
然後Father 類稍做修改就可以看到美化後的成績單,看代碼如下:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 老爸看成績單了
*/
public class Father {
public static void main(String[] args) {
//美化過的成績單拿過來
SchoolReport sr= new SugarFouthGradeSchoolReport();
//看成績單
sr.report();
//然後老爸,一看,很開心,就簽名了
sr.sign("老三"); //我叫小三,老爸當然叫老三
}
}
運行結果如下:
這次考試語文最高是75,數學是78,自然是80
尊敬的XXX家長:
......
語文 62 數學65 體育 98 自然 63
.......
家長簽名:
您的設計模式
第 135 頁
我是排名第38名...
家長簽名爲:老三
通過繼承確實能夠解決這個問題,老爸看成績單很開心,然後就給簽字了,但是現實的情
況很複雜的,可能老爸聽我彙報最高成績後,就直接樂開花了,直接簽名了,後面的排名就沒必
要了,或者老爸要先聽排名情況,那怎麼辦?繼續擴展類?你能擴展多少個類?這還是一個比較
簡單的場景,一旦需要裝飾的條件非常的多,比如20 個,你還通過繼承來解決,你想想的子類
有多少個?你是不是馬上就要崩潰了!
好,你也看到通過繼承情況確實出現了問題,類爆炸,類的數量激增,光寫這些類不累死
你纔怪,而且還要想想以後維護怎麼辦,誰願意接收這麼一大堆類的維護哪?並且在面向對象的
設計中,如果超過2 層繼承,你就應該想想是不是出設計問題了,是不是應該重新找一條道了,
這是經驗值,不是什麼絕對的,繼承層次越多你以後的維護成本越多,問題這麼多,那怎麼辦?
好辦,裝飾模式出場來解決這些問題,我們先來看類圖:
增加一個抽象類和兩個實現類,其中Decorator 的作用是封裝SchoolReport 類,看源
代碼:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 裝飾類,我要把我的成績單裝飾一下
您的設計模式
第 136 頁
*/
public abstract class Decorator extends SchoolReport{
//首先我要知道是那個成績單
private SchoolReport sr;
//構造函數,傳遞成績單過來
public Decorator(SchoolReport sr){
this.sr = sr;
}
//成績單還是要被看到的
public void report(){
this.sr.report();
}
//看完畢還是要簽名的
public void sign(String name){
this.sr.sign(name);
}
}
Decorator 抽象類的目的很簡單,就是要讓子類來對封裝SchoolReport 的子類,怎麼
封裝?重寫report 方法!先看HighScoreDecorator 實現類:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 我要把我學校的最高成績告訴老爸
*/
public class HighScoreDecorator extends Decorator {
//構造函數
public HighScoreDecorator(SchoolReport sr){
super(sr);
}
//我要彙報最高成績
private void reportHighScore(){
您的設計模式
第 137 頁
System.out.println("這次考試語文最高是75,數學是78,自然是80");
}
//最高成績我要做老爸看成績單前告訴他,否則等他一看,就掄起笤帚有揍我,我那
還有機會說呀
@Override
public void report(){
this.reportHighScore();
super.report();
}
}
重寫了report 方法,先調用具體裝飾類的裝飾方法reportHighScore,然後再調用具
體構件的方法,我們再來看怎麼回報學校排序情況SortDecorator 代碼:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 學校排名的情況彙報
*/
public class SortDecorator extends Decorator {
//構造函數
public SortDecorator(SchoolReport sr){
super(sr);
}
//告訴老爸學校的排名情況
private void reportSort(){
System.out.println("我是排名第38名...");
}
//老爸看完成績單後再告訴他,加強作用
@Override
public void report(){
super.report();
this.reportSort();
}
}
您的設計模式
第 138 頁
然後看看我老爸怎麼看成績單的:
package com.cbf4life;
/**
* @author cbf4Life [email protected]
* I'm glad to share my knowledge with you all.
* 老爸看成績單了
*/
public class Father {
public static void main(String[] args) {
//成績單拿過來
SchoolReport sr;
sr = new FouthGradeSchoolReport(); //原裝的成績單
//加了最高分說明的成績單
sr = new HighScoreDecorator(sr);
//又加了成績排名的說明
sr = new SortDecorator(sr);
//看成績單
sr.report();
//然後老爸,一看,很開心,就簽名了
sr.sign("老三"); //我叫小三,老爸當然叫老三
}
}
老爸一看成績單,聽我這麼一說,非常開心,兒子有進步呀,從40 多名進步到30 多名,
進步很大,躲過了一頓海扁。
這就是裝飾模式,裝飾模式的通用類圖如下:
您的設計模式
第 139 頁
看類圖,Component 是一個接口或者是抽象類,就是定義我們最核心的對象,也就是最
原始的對象,比如上面的成績單,記住在裝飾模式中,必然有一個被提取出來最核心、最原
始、最基本的接口或抽象類,就是Component。
ConcreteComponent 這個事最核心、最原始、最基本的接口或抽象類的實現,你要裝飾
的就是這個東東。
Decorator 一般是一個抽象類,做什麼用呢?實現接口或者抽象方法,它裏面可不一定
有抽象的方法呀,在它的屬性裏必然有一個private 變量指向Component。
ConcreteDecoratorA 和ConcreteDecoratorB 是兩個具體的裝飾類,你要把你最核心的、
最原始的、最基本的東西裝飾城啥東西,上面的例子就是把一個比較平庸的成績單裝飾成家
長認可的成績單。
裝飾模式是對繼承的有力補充,你要知道繼承可不是萬能的,繼承可以解決實際的問題,
但是在項目中你要考慮諸如易維護、易擴展、易複用等,而且在一些情況下(比如上面那個
成績單例子)你要是用繼承就會增加很多了類,而且靈活性非常的差,那當然維護也不容易
了,也就是說裝飾模式可以替代繼承,解決我們類膨脹的問題,你要知道繼承是靜態的給類
增加功能, 而裝飾模式則是動態的給增加功能, 你看上面的那個例子, 我不想要
SortDecorator 這層的封裝也很簡單呀,直接在Father 中去掉就可以了,如果你用繼承就
必須修改程序。
裝飾模式還有一個非常好的優點,擴展性非常好,在一個項目中,你會有非常多因素考
慮不到,特別是業務的變更,時不時的冒出一個需求,特別是提出一個令項目大量延遲的需
求時候,那種心情是…,真想罵娘!裝飾模式可以給我們很好的幫助,通過裝飾模式重新封
您的設計模式
第 140 頁
裝一個類,而不是通過繼承來完成,簡單點說,三個繼承關係Father,Son,GrandSon 三個類,
我要再Son 類上增強一些功能怎麼辦?我想你會堅決的頂回去!不允許,對了,爲什麼呢?
你增強的功能是修改Son 類中的方法嗎?增加方法嗎 ?對GrandSon 的影響哪?特別是
GrandSon 有多個的情況,你怎麼辦?這個評估的工作量就是夠你受的,所以這個是不允許
的,那還是要解決問題的呀,怎麼辦?通過建立SonDecorator 類來修飾Son,等於說是創
建了一個新的類,這個對原有程序沒有變更,通過擴充很好的完成了這次變更。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章