設計模式-裝飾器模式


微信關注公眾號 JavaStorm 獲取最新內容。

裝飾器模式(Decorator),動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾器模式比生成子類更為靈活;它允許向一個現有的對象添加新的功能,同時又不改變其結構。裝飾器模式屬於結構型模式

UML 類圖

裝飾器模式

  • Component:接口,定義一個抽象接口裝飾對象與真實對象具有相同的接口,以便裝飾器動態的添加職責。
  • ConcreteComponent: 接口的具體對象。
  • Decorator:裝飾類,繼承了 Component , 從外類來拓展 Component 的功能 並且持有一個 Component 的引用,通過構造器實例化,從而實現對真實對象的職責裝飾增強。
  • ConcreteDecorator:具體裝飾類,用於給實際對象添加職責。

使用場景

現在有一個場景:煎餅果子,科技園上班族早上去買煎餅果子(Pancake),有的人要加雞蛋 (Egg)、有的人加火腿 (Ham)、有的人加生菜 (Lettuce)。有的土豪煎餅果子來一套全都要。現在我們來定義煎餅烹飪實現。(ps:留一個功能讀者自己實現:不同的套餐價格是不一樣的,如何計算出不同煎餅果子的價格?有興趣的讀者可以留言或者微信公眾號后台留言)。

代碼實現

代碼可以左右滑動

  1. 先定義煎餅接口也就是我們的被裝飾類,以及烹飪的方法 。
package com.zero.headfirst.decorator;

public interface Pancake {
    /**
     * 烹飪方法
     */
    void cook();
}
  1. 定義一個乞丐版煎餅,被裝飾對象。
package com.zero.headfirst.decorator;

/**
 * 被裝飾對象:定義最基本的乞丐版煎餅,啥都沒加
 */
public class BeggarPancake implements Pancake {
    @Override
    public void cook() {
        System.out.println("乞丐版基本煎餅");
    }
}
  1. 定義抽象裝飾類 煎餅果子裝飾器 PancakeDecorator:抽象裝飾器角色,實現煎餅接口(被裝飾器接口),持有被裝飾器的引用 (pancake)將烹飪行為轉發具體的裝飾器。
package com.zero.headfirst.decorator;

/**
 * 抽象裝飾器角色,實現煎餅接口(被裝飾器接口),持有被裝飾器的引用將烹飪行為轉發具體的裝飾器。
 */
public abstract class PancakeDecorator implements Pancake {

    private Pancake pancake;

    public PancakeDecorator(Pancake pancake) {
        this.pancake = pancake;
    }

    @Override
    public void cook() {
        if (pancake != null) {
            pancake.cook();
        }
    }
}
  1. 各種具體裝飾類對乞丐版煎餅進行不等級別的土豪加工。首先繼承 抽象出來的 PancakeDecorator ,重寫 cook 方法,實現加工。

EggDecorator 雞蛋裝飾器:繼承 PancakeDecorator,重寫 cook 方法。動態添加雞蛋,然后調用pancake 的cook。

package com.zero.headfirst.decorator;

/**
 * 雞蛋裝飾器:覆蓋cook方法,加入自身的實現,並且調用父類的cook方法,也就是構造函數中
 * EggDecorator(Pancake pancake),這里傳入的pancake的cook操作
 */
public class EggDecorator extends PancakeDecorator {
    public EggDecorator(Pancake pancake) {
        super(pancake);
    }

    @Override
    public void cook() {
        System.out.println("加一個雞蛋;");
        super.cook();
    }
}

火腿裝飾器: HamDecorator

package com.zero.headfirst.decorator;

/**
 * 火腿裝飾器
 */
public class HamDecorator extends PancakeDecorator {

    public HamDecorator(Pancake pancake) {
        super(pancake);
    }

    @Override
    public void cook() {
        System.out.println("加一個火腿;");
        super.cook();
    }

}

生菜裝飾器

package com.zero.headfirst.decorator;

/**
 * 生菜裝飾器
 */
public class LettuceDecorator extends PancakeDecorator {

    public LettuceDecorator(Pancake pancake) {
        super(pancake);
    }

    @Override
    public void cook() {
        System.out.println("加生菜;");
        super.cook();
    }

}

  1. 定義一個煎餅果子攤位。
package com.zero.headfirst.decorator;

/**
 * 煎餅果子店
 */
public class PancakeShop {
    public static void main(String[] args) {
        System.out.println("========土豪來了,全都加上。======");
        BeggarPancake beggarPancake = new BeggarPancake();
        EggDecorator eggDecorator = new EggDecorator(beggarPancake);
        HamDecorator hamAndEggDecorator = new HamDecorator(eggDecorator);
        LettuceDecorator lettuceAndHamAndEggDecorator = new LettuceDecorator(hamAndEggDecorator);
        lettuceAndHamAndEggDecorator.cook();

        System.out.println("========苦逼碼農來了,只要雞蛋補補腦。=====");
        BeggarPancake beggarPancake1 = new BeggarPancake();
        EggDecorator eggDecorator1 = new EggDecorator(beggarPancake1);
        eggDecorator1.cook();
    }
}

  1. 運行結果
========土豪來了,全都加上。======

加生菜;

加一個火腿;

加一個雞蛋;

乞丐版基本煎餅

========苦逼碼農來了,只要雞蛋補補腦。=====

加一個雞蛋;

乞丐版基本煎餅

總結

真實世界的裝飾: Java I/O。
注意事項與要點

  • 抽象裝飾器與具體被裝飾對象實現同一個接口。
  • 抽象裝飾器持有被裝飾器接口對象,以便請求傳遞。
  • 具體裝飾器需要重寫抽象裝飾器的方法並引用super進行條用,轉發請求。

適用場景

  • 拓展一個類的功能。

  • 動態的添加與撤銷職責。

優點

  • 裝飾類與被裝飾類只關心自己的核心邏輯,實現了解耦。
  • 方便動態拓展,開閉原則。且比繼承靈活。

缺點

  • 如果功能拓展太多,將產生大量的類。
  • 多層裝飾會變得復雜。

以上代碼可參考我的 GitHub : https://github.com/UniqueDong/zero-design-stu。headfirst包下。歡迎關注公眾號: JavaStorm 獲取最新文章,也可在后台留言提出意見。收藏與關注是最大的鼓勵。

JavaStorm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM