1. 程式人生 > >二十、裝飾設計模式

二十、裝飾設計模式

1. 裝飾設計模式介紹

定義

動態地給一個物件新增一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。

2. 裝飾設計模式使用場景

動態地給一個物件新增一些額外的職責。

3. 裝飾設計模式UML類圖

裝飾設計模式UML

角色介紹:

  • Component:抽象元件,充當的是被裝飾的原始物件。
  • ConcreteComponent:元件具體實現類。
  • Decotor:抽象裝飾類:職責就是為了裝飾元件物件,內部有一個指向元件物件的引用。
  • ConcreteDecoratorA:裝飾者的具體實現類
  • Client:測試類

4. 裝飾設計模式簡單實現

  • (1)、首先定義一個抽象元件類:
public abstract class Component {

    /**
     * 抽象的方法
     */
    public abstract void operation();
}

抽象元件類裡面只有一個抽象方法operation()

  • (2)、元件具體實現類:
public class ConcreteComponent extends Component {
    @Override
    public void operation() {
    }
}

元件具體實現類繼承自組建抽象類,在operation方法裡面有具體的實現邏輯。

  • (3)、抽象裝飾者:
public class Decorator extends Component {
    private Component component;//持有一個component物件的引用

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

抽象裝飾者類也繼承自Component類,在建構函式裡面接收一個Component型別的Component物件的引用,在operation()方法裡面呼叫component的operation方法。

  • (4)、裝飾者實現類:
    ConcreteDecoratorA 和 ConcreteDecoratorB只是operation方法呼叫父類operation方法的順便不同而已。
public class ConcreteDecoratorA extends Decorator {

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

    @Override
    public void operation() {
        opetateA();
        super.operation();
        opetateB();
    }

    private void opetateB() {

    }

    private void opetateA() {

    }
}   

裝飾者實現類裡面,繼承自Decorator類,在operation方法裡面會呼叫父類operation方法,也可以自己進行相關邏輯操作。

  • (5)測試類:
public class Client {
    public static void main(String[] args) {
        //構造被裝飾的元件物件
        ConcreteComponent concreteComponent = new ConcreteComponent();
        //構造裝飾者物件A,並呼叫
        ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(concreteComponent);
        concreteDecoratorA.operation();
        System.out.println("----");
        //構造裝飾者物件B,並呼叫
        ConcreteDecoratorA concreteDecoratorB = new ConcreteDecoratorA(concreteComponent);
        concreteDecoratorB.operation();
    }
}

為了讓大家更好的理解裝飾者設計模式,現在有如下場景:

比如我們現在要去買一份炒飯,如果什麼都不加的話,只要五元;加青椒的話,多收五元;加肉絲的話多收三元。

  • (1)、米飯基類:
public abstract class Rice {
    private String name;

    public String getName() {
        return name;
    }

    public abstract int getPrice();
}

米飯基類裡面有一個名稱和一個價格:

  • (2)、炒飯基類:
public class FryRice extends Rice {
    public FryRice() {
    }

    @Override
    public String getName() {
        return "炒飯";
    }

    @Override
    public int getPrice() {
        return 5;
    }
}

炒飯返回的價格是5元

  • (3)、配料類,相當於上面的裝飾類
public class Ingredient extends Rice {
    private Rice rice;

    public Ingredient(Rice rice) {
        this.rice = rice;
    }

    @Override
    public String getName() {
        return rice.getName();
    }

    @Override
    public int getPrice() {
        return rice.getPrice();
    }
}
  • (4)、火腿配料
public class Ham extends Ingredient {
    public Ham(Rice rice) {
        super(rice);
    }

    @Override
    public String getName() {
        return super.getName() + ",加火腿";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 3;
    }
}
  • (5)、瘦肉配料:
public class Lean extends Ingredient {
    public Lean(Rice rice) {
        super(rice);
    }

    @Override
    public String getName() {
        return super.getName() + ",加瘦肉";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 4;
    }
}
  • (6)、測試類:
public class Client {
    public static void main(String[] args) {
        //炒飯基類
        FryRice fryRice = new FryRice();
        System.out.println(fryRice.getName() + "," + fryRice.getPrice());

        //瘦肉炒飯基類
        Lean leanFryRice = new Lean(fryRice);
        System.out.println(leanFryRice.getName() + "," + leanFryRice.getPrice());

        //瘦肉火腿炒飯
        Ham ham = new Ham(leanFryRice);
        System.out.println(ham.getName() + "," + ham.getPrice());

    }
}

測試結果如下:

    炒飯,5
    炒飯,加瘦肉,9
    炒飯,加瘦肉,加火腿,12

5. 裝飾設計模式在Android原始碼中

在Android中,我們會經常在Activity裡面使用startActivity()方法啟動一個元件。

startActivity這個方法最開始在Context中定義,Context是一個抽象類,裡面定義了許多抽象方法。Context相當於裝飾設計模式裡面的抽象元件。

startActivity具體實現在ContextImpl中完成的。ContextImpl相當於元件的具體實現。

Activity繼承自ContextThemeWrapper,ContextWrapper又繼承自Context。

在ContextWrapper裡面有如下程式碼:

Context mBase;
public ContextWrapper(Context base) {
    mBase = base;
}
@Override
public void startActivity(Intent intent) {
    mBase.startActivity(intent);
}

可見這裡的ContextWrapper就是我們裝飾者。

最後給一下幾個類的關係:

裝飾設計模式Android原始碼中

6. 總結

  • 優點:
    • 裝飾模式與繼承關係的目的都是要擴充套件物件的功能,但是裝飾模式可以提供比繼承更多的靈活性。繼承關係是靜態,在系統執行前就決定了;裝飾設計模式允許動態決定新增或者刪除這些“裝飾”。
    • 通過不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合。
  • 缺點:
    • 產生多餘的類。