裝飾模式【Decorator Pattern】
裝飾模式顧名思義就是將程序裝飾進行裝飾完成我們所想要的效果。山寨手機需要裝飾,我們的程序有時候也需要進行裝飾。
下面以向家長彙報分數讓家長簽字爲例。先看下最初的類圖。
倘若這樣子直接拿給老爸看(直接彙報成績,要簽名),那豈不是要挨板子。對於差生來講,還得先裝飾一下好。
下面再看一下改善後的類圖。
這樣子設計的話,就可以在report()方法中做點手腳了,可以先彙報一下最高分,畢竟老子跟最高分差了10來分而已。然後可以報告一下排名,畢竟全班60人我考了38,(其實已經退學了快20人了)。
程序設計如下:
public class SugarFouthGradeSchoolReport extends FouthGradeSchoolReport {
//首先要定義你要美化的方法,先給老爸說學校最高成績
private void reportHighScore(){
System.out.println("這次考試語文最高是75,數學是78,自然是80");
}
//在老爸看完畢成績單後,我再彙報學校的排名情況
private void reportSort(){
System.out.println("我是排名第38名...");
}
//由於彙報的內容已經發生變更,那所以要重寫父類
@Override
public void report(){
this.reportHighScore(); //先說最高成績
super.report(); //然後老爸看成績單
this.reportSort(); //然後告訴老爸學習學校排名
}
}
這樣老爸看到的成績單已經是美化過的了:
public class Father {
public static void main(String[] args) {
//美化過的成績單拿過來
SchoolReport sr= new SugarFouthGradeSchoolReport();
//看成績單
sr.report();
//然後老爸,一看,很開心,就簽名了
sr.sign("老三"); //我叫小三,老爸當然叫老三
}
上面的例子通過繼承來解決這個問題,通過繼承只實現裝飾最高分這一項,倘若我們要裝飾得條件很多,那就會出問題了,出現類爆炸的情況。想想維護起來的成本。。。
我們還得好好裝飾一下,先看改善後的類圖:
增加一個抽象類和兩個實現類,其中 Decorator 的作用是封裝 SchoolReport 類。
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 進行封裝。看下這麼封裝:
public class HighScoreDecorator extends Decorator {
//構造函數
public HighScoreDecorator(SchoolReport sr){
super(sr);
}
//我要彙報最高成績
private void reportHighScore(){
System.out.println("這次考試語文最高是75,數學是78,自然是80");
}
//最高成績我要做老爸看成績單前告訴他,否則等他一看,就掄起笤帚有揍我,我那還有機會說呀
@Override
public void report(){
this.reportHighScore();
super.report();
}
}
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();
}
}
上面兩個子類重寫了report方法,達到了想要的結果。
老爸看成績單:
SchoolReport sr;
sr = new FouthGradeSchoolReport(); //原裝的成績單
//加了最高分說明的成績單
sr = new HighScoreDecorator(sr);
//又加了成績排名的說明
sr = new SortDecorator(sr);
//看成績單
sr.report();
//然後老爸,一看,很開心,就簽名了
sr.sign("老三"); //我叫小三,老爸當然叫老三
通過這樣的裝飾模式就躲過了一頓海扁。
下面再看看其通用類圖。
看類圖,Component 是一個接口或者是抽象類,就是定義我們最核心的對象,也就是最原始的對象,比
如上面的成績單,記住在裝飾模式中,必然有一個被提取出來最核心、最原始、最基本的接口或抽象類,就是 Component。
ConcreteComponent 這個事最核心、 最原始、 最基本的接口或抽象類的實現, 你要裝飾的的對象。
Decorator 一般是一個抽象類,實現接口或者抽象方法。
ConcreteDecoratorA 和 ConcreteDecoratorB 是兩個具體的裝飾類。
總結:
(1)裝飾者模式的存在,解決的問題主要就是繼承帶來的弊端,首先可能帶來的類是上面所說的類爆炸的問題。同時繼承的話由於子類必須繼承父類的方法,所以當中間類的方法改變時,那麼就會牽一髮動全身了,利用裝飾模式就可以將其進行分離,降低其耦合性,方便程序的擴展。
(2)繼承與裝飾模式的選取:選擇繼承還是裝飾模式時主要考慮類之間的繼承層數問題,如果只是一層,那可以考慮繼承,如果出現多層繼承的情況,那就應該考慮裝飾模式了。