天天看點

設計模式-裝飾器模式

設計模式-裝飾器模式就這麼玩,一文秒懂

微信關注公衆号 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進行條用,轉發請求。

适用場景

優點

  • 裝飾類與被裝飾類隻關心自己的核心邏輯,實作了解耦。
  • 友善動态拓展,開閉原則。且比繼承靈活。

缺點

  • 如果功能拓展太多,将産生大量的類。
  • 多層裝飾會變得複雜。

繼續閱讀