1. 程式人生 > >設計模式(3)裝飾者模式

設計模式(3)裝飾者模式

number 設計模式 bsp bubuko override closed des ase spa

我們到咖啡店喝咖啡的時候,往往會根據各自的口味去選擇咖啡和各種配料,比如咖啡可以選擇綜合、焙、低咖啡因、濃縮,配料可以選搭牛奶、摩卡、豆漿、奶泡。這個情境下就可以使用裝飾者模式,用配料對咖啡進行裝飾,組合成不同的咖啡。

裝飾者模式——動態的將職責附加到對象上。想要擴展功能,裝飾者模式是有別於繼承的另一種選擇。

裝飾者和被裝飾者對象有相同的超類,可以用一個或多個裝飾者裝飾一個對象,裝飾者可以在被裝飾者的行為之前或之後,加上自己的行為,從而達到特定的目的。

比如咖啡和配料都繼承於一個超類Beverage,首先最底層是一個咖啡對象,然後一層層的用配料去裝飾咖啡對象,最終得到了一個被裝飾完成的對象。

技術分享圖片

下面看下代碼

裝飾者和被裝飾在的超類

public abstract class Beverage {

    /**
     * 大小
     */
    private SizeEnum size;
    /**
     * 描述
     *
     * @return 描述
     */
    public abstract String getDescription();

    /**
     * 花費
     *
     * @return 花費
     */
    public abstract double cost();

    public SizeEnum getSize() {
        
return size; } public void setSize(SizeEnum size) { this.size = size; } }

裝飾者的父類

public class CondimentDecorator extends Beverage{
    @Override
    public String getDescription() {
        return null;
    }

    @Override
    public double cost() {
        return 0;
    }
}

杯子大小的枚舉

public enum SizeEnum {

    /**
     * 小杯
     */
    TALL(1, "小杯"),
    /**
     * 中杯
     */
    GRANDE(2, "中杯"),
    /**
     * 大杯
     */
    VENTI(3, "大杯");

    SizeEnum(int value, String caption) {
        this.value = value;
        this.caption = caption;
    }

    /**
     * 大小
     */
    private int value;
    /**
     * 描述
     */
    private String caption;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public String getCaption() {
        return caption;
    }

    public void setCaption(String caption) {
        this.caption = caption;
    }
}

幾個繼承了超類的咖啡類

public class DarkRoast extends Beverage {

    public DarkRoast(SizeEnum sizeEnum) {
        super.setSize(sizeEnum);
    }

    @Override
    public String getDescription() {
        return "DarkRoast";
    }

    @Override
    public double cost() {
        return 0.99;
    }
}
技術分享圖片
public class Decaf extends Beverage {

    public Decaf(SizeEnum sizeEnum) {
        super.setSize(sizeEnum);
    }

    @Override
    public String getDescription() {
        return "Decaf";
    }

    @Override
    public double cost() {
        return 1.05;
    }
}
Decof 技術分享圖片
public class Espresso extends Beverage {

    public Espresso(SizeEnum sizeEnum) {
        super.setSize(sizeEnum);
    }

    @Override
    public String getDescription() {
        return "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}
Espresso 技術分享圖片
public class HouseBland extends Beverage {

    public HouseBland(SizeEnum sizeEnum) {
        super.setSize(sizeEnum);
    }

    @Override
    public String getDescription() {
        return "HouseBland";
    }

    @Override
    public double cost() {
        return 0.89;
    }
}
HouseBland

裝飾類

public class Milk extends CondimentDecorator {

    private Beverage beverage;

    public Milk(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return "Milk, " + beverage.getDescription();
    }

    @Override
    public double cost() {
        return beverage.cost() + 0.10;
    }

    @Override
    public SizeEnum getSize() {
        return beverage.getSize();
    }
}
技術分享圖片
public class Mocha extends CondimentDecorator {

    private Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return "Mocha, " + beverage.getDescription();
    }

    @Override
    public double cost() {
        return beverage.cost() + 0.20;
    }

    @Override
    public SizeEnum getSize() {
        return beverage.getSize();
    }
}
Mocha 技術分享圖片
public class Soy extends CondimentDecorator {

    private Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return "Soy, " + beverage.getDescription();
    }

    @Override
    public double cost() {
        switch (this.getSize()) {
            case TALL:
                return beverage.cost() + 0.10;
            case VENTI:
                return beverage.cost() + 0.15;
            case GRANDE:
                return beverage.cost() + 0.20;
            default:
                return beverage.cost() + 0.10;
        }
    }

    @Override
    public SizeEnum getSize() {
        return beverage.getSize();
    }
}
Soy 技術分享圖片
public class Whip extends CondimentDecorator {

    private Beverage beverage;

    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return "Whip, " + beverage.getDescription();
    }

    @Override
    public double cost() {
        return beverage.cost() + 0.10;
    }

    @Override
    public SizeEnum getSize() {
        return beverage.getSize();
    }
}
Whip

測試

public static void main(String[] args) {
        Beverage beverage1 = new Espresso(SizeEnum.GRANDE);
        System.out.println(beverage1.getSize().getCaption() + " " + beverage1.getDescription() + "  cost: " + beverage1.cost());
        Beverage beverage2 = new DarkRoast(SizeEnum.VENTI);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Milk(beverage2);
        System.out.println(beverage2.getSize().getCaption() + " " + beverage2.getDescription() + "  cost: " + beverage2.cost());
    }

輸出

技術分享圖片

裝飾者模式的運用有很多,例如Java I/O 中 BufferedInputStream, LineNumberInputStream擴展自FileInputStream,而FileInputStream是一個抽象的裝飾類。

裝飾者模式給我們帶來了一種新的視角,除了使用繼承,裝飾者模式也可以對行為進行擴展。可以用無數的裝飾者包裝一個組件,但是裝飾者模式會導致設計中出現許多的小對象,如果過渡使用的話,會讓程序變得異常復雜。

設計模式(3)裝飾者模式