《Head First 設計模式》:裝飾者模式

正文

一、定義

裝飾者模式動態地將責任(功能)附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。

要點:

  • 裝飾者和被裝飾者有相同的超類型。
  • 可以用一個或多個裝飾者包裝一個對象。
  • 既然裝飾者和被裝飾者有相同的超類型,所以在任何需要原始對象(被裝飾者)的場合,都可以用裝飾過的對象代替它。
  • 裝飾者可以在被裝飾者的行爲之前與/或之後,加上自己的行爲,甚至將被裝飾者的行爲整個取代掉,以到達特定的目的。
  • 對象可以在任何時候被裝飾,所以可以在運行時動態地、不限量地用裝飾者裝飾對象。
  • 裝飾者會導致設計中出現許多小對象,如果過度使用,會讓程序變得很複雜。

二、實現步驟

1、創建組件接口

裝飾者和被裝飾者都必須實現組件接口。

也可以用組件抽象類,然後讓裝飾者和被裝飾者繼承組件抽象類,只要裝飾者和被裝飾者具有相同的超類型即可。

/**
 * 組件接口(裝飾者和被裝飾者都必須實現該接口)
 */
public interface Component {

    public void doSomething();
}

2、創建具體的組件,並實現組件接口

/**
 * 具體組件(被裝飾者)
 */
public class ConcreteComponent implements Component {

    @Override
    public void doSomething() {
        System.out.println("ConcreteComponent do something...");
    }
}

3、創建裝飾者抽象類,並實現組件接口

如果只有一個裝飾者,也可以不創建裝飾者抽象類,而是由具體的裝飾者直接實現組件接口。

/**
 * 組件裝飾者抽象類
 */
public abstract class ComponentDecorator implements Component {
    
    protected Component component;
    
    public ComponentDecorator(Component component) {
        // 通過構造傳入組件(被裝飾者)
        this.component = component;
    }

    @Override
    public void doSomething() {
        // 委託給組件(被裝飾者)
        component.doSomething();
    }
}

4、創建具體的裝飾者,並繼承裝飾者抽象類

(1)裝飾者 A

/**
 * 裝飾者A
 */
public class ComponentDecoratorA extends ComponentDecorator {

    public ComponentDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void doSomething() {
        // 裝飾者添加自己的業務代碼
        
        component.doSomething();
        
        // 裝飾者添加自己的業務代碼
        System.out.println("ComponentDecoratorA do something...");
    }
}

(2)裝飾者 B

/**
 * 裝飾者B
 */
public class ComponentDecoratorB extends ComponentDecorator {

    public ComponentDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void doSomething() {
        // 裝飾者添加自己的業務代碼
        
        component.doSomething();
        
        // 裝飾者添加自己的業務代碼
        System.out.println("ComponentDecoratorB do something...");
    }
}

5、使用裝飾者裝飾組件

public class Test {
    
    public static void main(String[] args) {
        // 具體組件(被裝飾者)
        Component component = new ConcreteComponent();
        // 用裝飾者A裝飾組件
        ComponentDecorator componentDecoratorA = new ComponentDecoratorA(component);
        // 用裝飾者B裝飾組件
        ComponentDecorator componentDecoratorB = new ComponentDecoratorB(component);
        
        component.doSomething();
        componentDecoratorA.doSomething();
        componentDecoratorB.doSomething();
    }
}

三、舉個栗子

1、背景

星巴茲是以擴張速度最快而聞名的咖啡連鎖店。因爲擴張速度實在太快了,他們準備更新訂單系統,以合乎他們的飲料供應要求——

顧客在購買咖啡時,可以要求在其中加入各種調料,例如:蒸奶、豆漿、摩卡(巧克力風味)或覆蓋奶泡。星巴茲會根據所加入的調料收取不同的費用。所以訂單系統必須考慮到這些調料部分。

2、實現

把調料理解爲飲料裝飾者,然後以飲料爲主體,用調料來“裝飾”飲料。

(1)創建飲料抽象類

/**
 * 飲料抽象類(組件)
 */
public abstract class Beverage {

    protected String description = "Unknown Beverage";
    
    /**
     * 描述
     */
    public String getDescription() {
        return description;
    }
    
    /**
     * 價格
     */
    public abstract double cost();
}

(2)創建具體的飲料,並繼承飲料抽象類

/**
 * 濃縮咖啡
 */
public class Espresso extends Beverage {
    
    public Espresso() {
        description = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}
/**
 * 綜合咖啡
 */
public class HouseBlend extends Beverage {
    
    public HouseBlend() {
        description = "House Blend Coffee";
    }

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

(3)創建調料抽象類,並繼承飲料抽象類

/**
 * 調料抽象類(裝飾者抽象類)
 */
public abstract class CondimentDecorator extends Beverage {

    @Override
    public abstract String getDescription();
}

(4)創建具體的調料,並繼承調料抽象類

/**
 * 摩卡(裝飾者)
 */
public class Mocha extends CondimentDecorator {
    
    Beverage beverage;
    
    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

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

    @Override
    public double cost() {
        // 加上摩卡的價格
        return beverage.cost() + 0.20;
    }
}
/**
 * 豆漿(裝飾者)
 */
public class Soy extends CondimentDecorator {
    
    Beverage beverage;
    
    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

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

    @Override
    public double cost() {
        // 加上豆漿的價格
        return beverage.cost() + 0.15;
    }
}
/**
 * 奶泡(裝飾者)
 */
public class Whip extends CondimentDecorator {
    
    Beverage beverage;
    
    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

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

    @Override
    public double cost() {
        // 加上奶泡的價格
        return beverage.cost() + 0.10;
    }
}

(5)測試

public class Test {
    
    public static void main(String[] args) {
        // 濃縮咖啡
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + " $" + beverage.cost());
        
        // 綜合咖啡 
        Beverage beverage2 = new HouseBlend();
        System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
        // 添加摩卡
        beverage2 = new Mocha(beverage2);
        // 添加豆漿
        beverage2 = new Soy(beverage2);
        // 添加奶泡
        beverage = new Whip(beverage2);
        System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章