設計模式第三講-裝飾者模式

小宇渣渣渣發表於2019-03-01

簡介

裝飾模式也算一種比較常見的設計模式,工作過程中很少刻意的去實現這種模式,因為這種模式也會帶來一些問題。

比如小類太多,組織起來比較麻煩,對客戶端完全可見,如果不瞭解各個類的功能會很亂等等

當然也有很多優點: 可以動態的給類增加一些職責,增加功能更加靈活,比起繼承來說也更加具有彈性

下面我們來看看它究竟是什麼樣子的

場景設定

以買車業務模型為例(本人未買過車,業務場景純屬臆想)

車裡面還有很多裝備可以選擇,根據不同的選擇裝飾決定最後到手的價格:

商品 價格
裸車 100000
加熱座椅(可選) 4000
真皮內飾(可選) 8000
蹦迪音響(可選) 5000

第一版本實現(我們在一個類裡面完成)

package car;

public class Car {

    //總價
    private int allPrice = 0;

    //裸車價格 --實際數值從資料庫查詢,此處略
    private int nakedPrice = 100000;

    //加熱座椅
    private int heatedSeat = 4000;

    //真皮內飾
    private int dermalInteriors = 8000;

    //蹦迪音響
    private int bengdiSound = 5000;


    public Car() {
        this.allPrice += this.nakedPrice;
    }

    /**
     * 新增真皮座椅
     */
    public void addHeatedSeat() {
        this.allPrice += this.heatedSeat;
    }

    /**
     * 新增真皮內飾
     */
    public void addDermalInteriors() {
        this.allPrice += this.dermalInteriors;
    }

    /**
     * 新增蹦迪音響
     */
    public void addBengdiSound() {
        this.allPrice += this.bengdiSound;
    }

    /**
     * 獲取總價
     *
     * @return
     */
    public int cost() {
        return this.allPrice;
    }

}

複製程式碼

客戶端呼叫:

package car;

public class Main {

    public static void main(String[] args) {

        Car car = new Car();

        car.addBengdiSound();
        System.out.println("總花費價格:" + car.cost());

    }

}

複製程式碼

output:

花費價格105000
複製程式碼

看起來很簡潔呢,並且也很好的實現了功能

首先這是我們的場景足夠的簡單,所涉及的配套元件也比較少,實際中可能針對某個方法可能有很複雜的獲取和計算邏輯,元件也會有很多種選擇,會造成類爆炸,無法繼續維護下去

如果我們使用裝飾模式,會帶來什麼改變呢,會給我們帶來什麼樣的體驗呢

設計:

設計模式第三講-裝飾者模式

實現:

宣告抽象類(被裝飾者和裝飾者繼承此類,目的對實現方法進行規正)

package car;

public abstract class Car {

    public String name;

    public abstract String cost();
}
複製程式碼

主角:被裝飾者類-裸車

package car;

/**
 * 被裝飾的類 -> 裸車
 */
public class NakeCar extends Car {

    //裸車價格 --實際數值從資料庫查詢,此處略
    private int nakedPrice = 100000;


    public NakeCar() {
        this.name = "裸車";
    }

    /**
     * 獲取總價
     *
     * @return
     */
    public String cost() {
        return this.name + ":" + this.nakedPrice;
    }
}
複製程式碼

具體裝飾類:

加熱座椅

package car;

/**
 * 裝飾元件
 */
public class HeatedSeat extends Car {

    //加熱座椅
    private int heatedSeat = 4000;

    //汽車物件
    private Car car;

    public HeatedSeat(Car car) {
        this.name = "加熱座椅";
        this.car = car;
    }

    /**
     * 獲取價格
     *
     * @return
     */
    public String cost() {
        //todo
        return this.car.cost() + "||" + this.name + ":" + this.heatedSeat;
    }

}

複製程式碼

真皮內飾

package car;

public class DermalInteriors extends Car {

    //真皮內飾
    private int dermalInteriors = 8000;

    //汽車物件
    private Car car;

    public DermalInteriors(Car car) {
        this.name = "真皮內飾";
        this.car = car;
    }

    /**
     * 獲取價格
     *
     * @return
     */
    public String cost() {
        //todo
        return this.car.cost() + "||" + this.name + ":" + this.dermalInteriors;
    }

}

複製程式碼

蹦迪音響

package car;

public class BengdiSound extends Car {

    //蹦迪音響
    private int bengdiSound = 5000;

    //汽車物件
    private Car car;

    public BengdiSound(Car car) {
        this.name = "蹦迪音響";
        this.car = car;
    }

    /**
     * 獲取價格
     *
     * @return
     */
    public String cost() {
        //todo
        return this.car.cost() + "||" + this.name + ":" + this.bengdiSound;
    }
}

複製程式碼

客戶端呼叫:

package car;

public class Main {

    public static void main(String[] args) {

        NakeCar car = new NakeCar();

        HeatedSeat heatedSeat = new HeatedSeat(car);
        BengdiSound bengdiSound = new BengdiSound(heatedSeat);
        System.out.println("總花費價格:" + bengdiSound.cost());
    }
}
複製程式碼

output:

總花費價格:裸車:100000||加熱座椅:4000||蹦迪音響:5000
複製程式碼

總結

  • 裝飾模式的優點:
    可以為已有的功能(被裝飾物件)動態的新增功能,簡化原有類。在我們的例子中, 裸車是主類,相當於業務中的核心,而一些動態的元件可以根據實際情況使用,也就相當於我們業務線中的附屬擴充功能。

    主邏輯和擴充邏輯的區分會使我們的程式碼更具擴充性。

  • 裝飾模式的缺點:
    我們可以看到裝飾模式的引入會增多類的數量,以及客戶端的使用會有複雜度上升。裝飾模式的類依賴特定的型別,需要嚴格遵守規約(抽象類的約束)

更多精彩內容關注公眾號:

設計模式第三講-裝飾者模式

相關文章