裝飾器模式:讓你的物件變得更強大

2023-09-06 15:00:31

在日常開發中,當需要給一個現有類新增附加職責,而又不能採用生成子類的方法進行擴充時。例如,該類被隱藏或者該類是終極類或者採用繼承方式會產生大量的子類。這時候,我們該怎麼辦呢?我們可以使用裝飾器器模式來解決這個問題,本文將從以下四個方面講解裝飾器器模式

  • 簡介
  • 優缺點
  • 應用場景
  • Java 程式碼範例、
  • Spring 程式碼範例

簡介

裝飾器模式(Decorator Pattern)是一種結構型設計模式,它可以在不改變現有物件的結構的情況下,動態地給物件增加一些額外的功能。裝飾器模式通過建立一個包裝物件(即裝飾器)來包裹真實物件,並在保持真實物件的介面不變的前提下,為其提供額外的功能。裝飾器模式可以在執行時根據需要選擇不同的裝飾器來組合和修改物件的行為。

  • Component(元件介面):所有被裝飾元件及裝飾器對應的介面標準,指定進行裝飾的行為方法。對應本章例程中的展示介面 Showable。
  • ConcreteComponent(元件實現):需要被裝飾的元件,實現元件介面標準,只具備自身未被裝飾的原始特性。對應本章例程中的女生類 Girl。
  • Decorator(裝飾器):裝飾器的高層抽象類,同樣實現元件介面標準,且包含一個被裝飾的元件。
  • ConcreteDecorator(裝飾器實現):繼承自裝飾器抽象類的具體子類裝飾器,可以有多種實現,在被裝飾元件物件的基礎上為其新增新的特性。對應本章例程中的粉底類 FoundationMakeup、口紅類 Lipstick。

推薦博主開源的 H5 商城專案waynboot-mall,這是一套全部開源的微商城專案,包含三個專案:運營後臺、H5 商城前臺和伺服器端介面。實現了商城所需的首頁展示、商品分類、商品詳情、商品 sku、分詞搜尋、購物車、結算下單、支付寶/微信支付、收單評論以及完善的後臺管理等一系列功能。 技術上基於最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中介軟體。分模組設計、簡潔易維護,歡迎大家點個 star、關注博主。

github 地址:https://github.com/wayn111/waynboot-mall

優缺點

裝飾器模式的優點有:

  • 裝飾器模式是繼承的有力補充,比繼承靈活,在不改變原有物件的情況下,動態地給一個物件擴充套件功能,隨插即用。
  • 通過使用不同的裝飾器及這些裝飾器的排列組合,可以實現不同效果。
  • 裝飾器模式完全遵守開閉原則,可以在不修改原有程式碼的基礎上增加新的功能。

裝飾器模式的缺點有:

  • 裝飾器模式會增加許多子類,過度使用會增加程式的複雜性。
  • 裝飾器模式會增加物件之間的聯絡,可能會引入迴圈參照的問題。
  • 裝飾器模式會影響物件的標識,當使用裝飾器對物件進行包裝時,物件的型別和行為可能會發生變化。

應用場景

裝飾器模式適用於以下場景:

  • 當需要給一個現有的類新增附加職責,而又不能採用繼承的方式時,可以使用裝飾器模式。例如,在不修改原有程式碼的情況下給一個視窗新增卷軸或者邊框等功能。
  • 當需要動態地給一個物件增加功能,而又需要復原該功能時,可以使用裝飾器模式。例如,在電子商務系統中根據使用者選擇的不同優惠券來計算商品價格時,可以使用裝飾器模式來實現。
  • 當需要為一批兄弟類進行改裝或加裝功能時,可以使用裝飾器模式。例如,在一個圖形介面工具箱中為多個不同的元件提供一些公共的功能時,可以使用裝飾器模式來實現。

java 程式碼範例

以下是一個實現裝飾器模式的 java 範例程式碼

  1. 定義了一個抽象元件介面 Shape 和兩個具體元件類 Circle 和 Rectangle,
//抽象元件介面
public interface Shape {
    void draw();
}

//具體元件類:圓形
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

//具體元件類:矩形
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}
  1. 定義一個抽象裝飾器類 ShapeDecorator 和兩個具體裝飾器類 RedShapeDecorator 和 GreenShapeDecorator,
//抽象裝飾器類
public abstract class ShapeDecorator implements Shape {
    //持有一個抽象元件物件
    protected Shape shape;

    //構造方法
    public ShapeDecorator(Shape shape) {
        this.shape = shape;
    }

    //呼叫被包裝物件的方法
    @Override
    public void draw() {
        shape.draw();
    }
}

//具體裝飾器類:紅色裝飾器
public class RedShapeDecorator extends ShapeDecorator {
    //構造方法
    public RedShapeDecorator(Shape shape) {
        super(shape);
    }

    //重寫draw方法,在呼叫被包裝物件的方法之前或之後新增新的功能
    @Override
    public void draw() {
        //呼叫被包裝物件的方法
        super.draw();
        //新增新的功能
        setRedBorder();
    }

    //定義新的功能方法
    private void setRedBorder() {
        System.out.println("Setting red border");
    }
}

//具體裝飾器類:綠色裝飾器
public class GreenShapeDecorator extends ShapeDecorator {
    //構造方法
    public GreenShapeDecorator(Shape shape) {
        super(shape);
    }

    //重寫draw方法,在呼叫被包裝物件的方法之前或之後新增新的功能
    @Override
    public void draw() {
        //呼叫被包裝物件的方法
        super.draw();
        //新增新的功能
        setGreenBorder();
    }

    //定義新的功能方法
    private void setGreenBorder() {
        System.out.println("Setting green border");
    }
}

  1. 編寫裝飾器模式測試程式碼,main 函數中建立了不同的元件和裝飾器物件,並呼叫了它們的方法,
//測試類
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        //建立一個圓形物件
        Shape circle = new Circle();
        //建立一個矩形物件
        Shape rectangle = new Rectangle();
        //建立一個紅色裝飾器物件,包裝圓形物件
        Shape redCircle = new RedShapeDecorator(circle);
        //建立一個綠色裝飾器物件,包裝矩形物件
        Shape greenRectangle = new GreenShapeDecorator(rectangle);

        //呼叫各個物件的方法,展示不同的效果
        System.out.println("Normal circle:");
        circle.draw();
        System.out.println("Normal rectangle:");
        rectangle.draw();
        System.out.println("Red circle:");
        redCircle.draw();
        System.out.println("Green rectangle:");
        greenRectangle.draw();
    }
}

輸出結果如下:

Normal circle:
Drawing a circle
Normal rectangle:
Drawing a rectangle
Red circle:
Drawing a circle
Setting red border
Green rectangle:
Drawing a rectangle
Setting green border

Spring 程式碼範例

要想再 Spring 專案中應用裝飾器模式,只需對以上程式碼進行簡單改造即可,

  1. 給具體元件類 Circle、Rectangle 新增 @Component 註解,
@Component
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

@Component
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}
  1. 給具體裝飾器類 RedShapeDecorator 和 GreenShapeDecorator 類新增 @Component 註解,
@Component
public class GreenShapeDecorator extends ShapeDecorator {
    // 構造方法
    public GreenShapeDecorator(@Qualifier("rectangle") Shape shape) {
        super(shape);
    }

    // 重寫draw方法,在呼叫被包裝物件的方法之前或之後新增新的功能
    @Override
    public void draw() {
        // 呼叫被包裝物件的方法
        super.draw();
        // 新增新的功能
        setGreenBorder();
    }

    // 定義新的功能方法
    private void setGreenBorder() {
        System.out.println("Setting green border");
    }
}

@Component
public class RedShapeDecorator extends ShapeDecorator {
    // 構造方法
    public RedShapeDecorator(@Qualifier("circle") Shape shape) {
        super(shape);
    }

    // 重寫draw方法,在呼叫被包裝物件的方法之前或之後新增新的功能
    @Override
    public void draw() {
        // 呼叫被包裝物件的方法
        super.draw();
        // 新增新的功能
        setRedBorder();
    }

    // 定義新的功能方法
    private void setRedBorder() {
        System.out.println("Setting red border");
    }
}
  1. 編寫 Spring 專案測試程式碼,
@SpringBootTest
@RunWith(SpringRunner.class)
public class DecoratorTest {

    // 從Spring容器中獲取Context物件
    @Autowired
    private RedShapeDecorator redCircle;
    @Autowired
    private GreenShapeDecorator greenRectangle;

    @Test
    public void test() {
        System.out.println("Red circle:");
        redCircle.draw();
        System.out.println("Green rectangle:");
        greenRectangle.draw();
    }
}

輸出結果如下:

Red circle:
Drawing a circle
Setting red border
Green rectangle:
Drawing a rectangle
Setting green border

總結

裝飾器模式可以將不同功能的單個模組規劃至不同的裝飾器類中,各裝飾器類獨立自主,各司其職。使用者端可以根據自己的需求自由搭配各種裝飾器,每加一層裝飾就會有新的特性體現出來,巧妙的設計讓功能模組層層疊加,裝飾之上套裝飾,最終使原始物件的特性動態地得到增強。

關注公眾號【waynblog】每週分享技術乾貨、開源專案、實戰經驗、國外優質文章翻譯等,您的關注將是我的更新動力!