2020重新出發(fā),JAVA設(shè)計模式 之九 裝飾模式

裝飾模式(裝飾設(shè)計模式)詳解

在現(xiàn)實生活中舌菜,常常需要對現(xiàn)有產(chǎn)品增加新的功能或美化其外觀萌壳,如房子裝修、相片加相框等日月。

在軟件開發(fā)過程中袱瓮,有時想用一些現(xiàn)存的組件。這些組件可能只是完成了一些核心功能爱咬。但在不改變其結(jié)構(gòu)的情況下尺借,可以動態(tài)地擴展其功能。

所有這些都可以釆用裝飾模式來實現(xiàn)精拟。

裝飾模式的定義與特點

裝飾(Decorator)模式的定義:指在不改變現(xiàn)有對象結(jié)構(gòu)的情況下燎斩,動態(tài)地給該對象增加一些職責(zé)(即增加其額外功能)的模式虱歪,它屬于對象結(jié)構(gòu)型模式。

裝飾(Decorator)模式的主要優(yōu)點有:

  • 采用裝飾模式擴展對象的功能比采用繼承方式更加靈活栅表。
  • 可以設(shè)計出多個不同的具體裝飾類笋鄙,創(chuàng)造出多個不同行為的組合。

其主要缺點是:裝飾模式增加了許多子類怪瓶,如果過度使用會使程序變得很復(fù)雜萧落。

裝飾模式的結(jié)構(gòu)與實現(xiàn)

通常情況下,擴展一個類的功能會使用繼承方式來實現(xiàn)洗贰。但繼承具有靜態(tài)特征找岖,耦合度高,并且隨著擴展功能的增多哆姻,子類會很膨脹宣增。如果使用組合關(guān)系來創(chuàng)建一個包裝對象(即裝飾對象)來包裹真實對象,并在保持真實對象的類結(jié)構(gòu)不變的前提下矛缨,為其提供額外的功能,這就是裝飾模式的目標(biāo)帖旨。下面來分析其基本結(jié)構(gòu)和實現(xiàn)方法箕昭。

1. 模式的結(jié)構(gòu)

裝飾模式主要包含以下角色。

  1. 抽象構(gòu)件(Component)角色:定義一個抽象接口以規(guī)范準(zhǔn)備接收附加責(zé)任的對象解阅。
  2. 具體構(gòu)件(Concrete Component)角色:實現(xiàn)抽象構(gòu)件落竹,通過裝飾角色為其添加一些職責(zé)。
  3. 抽象裝飾(Decorator)角色:繼承抽象構(gòu)件货抄,并包含具體構(gòu)件的實例述召,可以通過其子類擴展具體構(gòu)件的功能。
  4. 具體裝飾(ConcreteDecorator)角色:實現(xiàn)抽象裝飾的相關(guān)方法蟹地,并給具體構(gòu)件對象添加附加的責(zé)任积暖。

裝飾模式的結(jié)構(gòu)圖如圖 1 所示。

裝飾模式的結(jié)構(gòu)圖

? 圖1 裝飾模式的結(jié)構(gòu)圖

2. 模式的實現(xiàn)

裝飾模式的實現(xiàn)代碼如下:

package decorator;
public class DecoratorPattern
{
    public static void main(String[] args)
    {
        Component p=new ConcreteComponent();
        p.operation();
        System.out.println("---------------------------------");
        Component d=new ConcreteDecorator(p);
        d.operation();
    }
}
//抽象構(gòu)件角色
interface  Component
{
    public void operation();
}
//具體構(gòu)件角色
class ConcreteComponent implements Component
{
    public ConcreteComponent()
    {
        System.out.println("創(chuàng)建具體構(gòu)件角色");       
    }   
    public void operation()
    {
        System.out.println("調(diào)用具體構(gòu)件角色的方法operation()");           
    }
}
//抽象裝飾角色
class Decorator implements Component
{
    private Component component;   
    public Decorator(Component component)
    {
        this.component=component;
    }   
    public void operation()
    {
        component.operation();
    }
}
//具體裝飾角色
class ConcreteDecorator extends Decorator
{
    public ConcreteDecorator(Component component)
    {
        super(component);
    }   
    public void operation()
    {
        super.operation();
        addedFunction();
    }
    public void addedFunction()
    {
        System.out.println("為具體構(gòu)件角色增加額外的功能addedFunction()");           
    }
}

程序運行結(jié)果如下:

創(chuàng)建具體構(gòu)件角色
調(diào)用具體構(gòu)件角色的方法operation()
---------------------------------
調(diào)用具體構(gòu)件角色的方法operation()
為具體構(gòu)件角色增加額外的功能addedFunction()

裝飾模式的應(yīng)用實例

【例1】用裝飾模式實現(xiàn)游戲角色“莫莉卡·安斯蘭”的變身怪与。

分析:在《惡魔戰(zhàn)士》中夺刑,游戲角色“莫莉卡·安斯蘭”的原身是一個可愛少女,但當(dāng)她變身時分别,會變成頭頂及背部延伸出蝙蝠狀飛翼的女妖遍愿,當(dāng)然她還可以變?yōu)榇┲镣庖碌纳倥_@些都可用裝飾模式來實現(xiàn)耘斩,在本實例中的“莫莉卡”原身有 setImage(String t) 方法決定其顯示方式沼填,而其 變身“蝙蝠狀女妖”和“著裝少女”可以用 setChanger() 方法來改變其外觀,原身與變身后的效果用 display() 方法來顯示(點此下載其原身和變身后的圖片)括授,圖 2 所示是其結(jié)構(gòu)圖坞笙。

游戲角色“莫莉卡·安斯蘭”的結(jié)構(gòu)圖

圖2 游戲角色“莫莉卡·安斯蘭”的結(jié)構(gòu)圖

程序代碼如下:

package decorator;
import java.awt.*;
import javax.swing.*;
public class MorriganAensland
{
    public static void main(String[] args)
    {
        Morrigan m0=new original();
        m0.display();
        Morrigan m1=new Succubus(m0);
        m1.display();
        Morrigan m2=new Girl(m0);
        m2.display();
    }
}
//抽象構(gòu)件角色:莫莉卡
interface  Morrigan
{
    public void display();
}
//具體構(gòu)件角色:原身
class original extends JFrame implements Morrigan
{
    private static final long serialVersionUID = 1L;
    private String t="Morrigan0.jpg";
    public original()
    {
        super("《惡魔戰(zhàn)士》中的莫莉卡·安斯蘭");                
    }
    public void setImage(String t)
    {
        this.t=t;           
    }
    public void display()
    {   
        this.setLayout(new FlowLayout());
        JLabel l1=new JLabel(new ImageIcon("src/decorator/"+t));
        this.add(l1);   
        this.pack();       
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
        this.setVisible(true);
    }
}
//抽象裝飾角色:變形
class Changer implements Morrigan
{
    Morrigan m;   
    public Changer(Morrigan m)
    {
        this.m=m;
    }   
    public void display()
    {
        m.display();
    }
}
//具體裝飾角色:女妖
class Succubus extends Changer
{
    public Succubus(Morrigan m)
    {
        super(m);
    }   
    public void display()
    {
        setChanger();
        super.display();   
    }
    public void setChanger()
    {
        ((original) super.m).setImage("Morrigan1.jpg");           
    }
}
//具體裝飾角色:少女
class Girl extends Changer
{
    public Girl(Morrigan m)
    {
        super(m);
    }   
    public void display()
    {
        setChanger();
        super.display();   
    }
    public void setChanger()
    {
        ((original) super.m).setImage("Morrigan2.jpg");           
    }
}

程序運行結(jié)果如圖 3 所示轧邪。

游戲角色“莫莉卡·安斯蘭”的變身

? 圖3 游戲角色“莫莉卡·安斯蘭”的變身

裝飾模式的應(yīng)用場景

前面講解了關(guān)于裝飾模式的結(jié)構(gòu)與特點,下面介紹其適用的應(yīng)用場景羞海,裝飾模式通常在以下幾種情況使用忌愚。

  • 當(dāng)需要給一個現(xiàn)有類添加附加職責(zé),而又不能采用生成子類的方法進行擴充時却邓。例如硕糊,該類被隱藏或者該類是終極類或者采用繼承方式會產(chǎn)生大量的子類。
  • 當(dāng)需要通過對現(xiàn)有的一組基本功能進行排列組合而產(chǎn)生非常多的功能時腊徙,采用繼承關(guān)系很難實現(xiàn)简十,而采用裝飾模式卻很好實現(xiàn)。
  • 當(dāng)對象的功能要求可以動態(tài)地添加撬腾,也可以再動態(tài)地撤銷時螟蝙。

裝飾模式在 Java 語言中的最著名的應(yīng)用莫過于 Java I/O 標(biāo)準(zhǔn)庫的設(shè)計了。例如民傻,InputStream 的子類 FilterInputStream胰默,OutputStream 的子類 FilterOutputStream,Reader 的子類 BufferedReader 以及 FilterReader漓踢,還有 Writer 的子類 BufferedWriter牵署、FilterWriter 以及 PrintWriter 等,它們都是抽象裝飾類喧半。

下面代碼是為 FileReader 增加緩沖區(qū)而采用的裝飾類 BufferedReader 的例子:

BufferedReader in=new BufferedReader(new FileReader("filename.txtn));String s=in.readLine();

裝飾模式的擴展

裝飾模式所包含的 4 個角色不是任何時候都要存在的奴迅,在有些應(yīng)用環(huán)境下模式是可以簡化的,如以下兩種情況挺据。

(1) 如果只有一個具體構(gòu)件而沒有抽象構(gòu)件時取具,可以讓抽象裝飾繼承具體構(gòu)件,其結(jié)構(gòu)圖如圖 4 所示扁耐。

只有一個具體構(gòu)件的裝飾模式

圖4 只有一個具體構(gòu)件的裝飾模式

(2) 如果只有一個具體裝飾時暇检,可以將抽象裝飾和具體裝飾合并,其結(jié)構(gòu)圖如圖 5 所示做葵。

只有一個具體裝飾的裝飾模式

? 圖5 只有一個具體裝飾的裝飾模式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末占哟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酿矢,更是在濱河造成了極大的恐慌榨乎,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘫筐,死亡現(xiàn)場離奇詭異蜜暑,居然都是意外死亡,警方通過查閱死者的電腦和手機策肝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門肛捍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隐绵,“玉大人,你說我怎么就攤上這事拙毫∫佬恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵缀蹄,是天一觀的道長峭跳。 經(jīng)常有香客問我,道長缺前,這世上最難降的妖魔是什么蛀醉? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮衅码,結(jié)果婚禮上拯刁,老公的妹妹穿的比我還像新娘。我一直安慰自己逝段,他們只是感情好垛玻,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惹恃,像睡著了一般夭谤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巫糙,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音颊乘,去河邊找鬼参淹。 笑死,一個胖子當(dāng)著我的面吹牛乏悄,可吹牛的內(nèi)容都是我干的浙值。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼檩小,長吁一口氣:“原來是場噩夢啊……” “哼开呐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起规求,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤筐付,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阻肿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓦戚,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年丛塌,在試婚紗的時候發(fā)現(xiàn)自己被綠了较解。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畜疾。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖印衔,靈堂內(nèi)的尸體忽然破棺而出啡捶,到底是詐尸還是另有隱情,我是刑警寧澤奸焙,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布瞎暑,位于F島的核電站,受9級特大地震影響忿偷,放射性物質(zhì)發(fā)生泄漏金顿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一鲤桥、第九天 我趴在偏房一處隱蔽的房頂上張望揍拆。 院中可真熱鬧,春花似錦茶凳、人聲如沸嫂拴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筒狠。三九已至,卻和暖如春箱沦,著一層夾襖步出監(jiān)牢的瞬間辩恼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工谓形, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灶伊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓寒跳,卻偏偏與公主長得像聘萨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子童太,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355