在日常開發中,當需要給一個現有類添加附加職責,而又不能採用生成子類的方法進行擴充時。例如,該類被隱藏或者該類是終極類或者採用繼承方式會產生大量的子類。這時候,我們該怎麼辦呢?我們可以使用裝飾器器模式來解決這個問題,**本文將從以下四個方面講解裝飾器器模式**。 - 簡介 - 優缺點 - 應用場景 - ...
在日常開發中,當需要給一個現有類添加附加職責,而又不能採用生成子類的方法進行擴充時。例如,該類被隱藏或者該類是終極類或者採用繼承方式會產生大量的子類。這時候,我們該怎麼辦呢?我們可以使用裝飾器器模式來解決這個問題,本文將從以下四個方面講解裝飾器器模式。
- 簡介
- 優缺點
- 應用場景
- 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 示例代碼
- 定義了一個抽象組件介面 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");
}
}
- 定義一個抽象裝飾器類 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");
}
}
- 編寫裝飾器模式測試代碼,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 項目中應用裝飾器模式,只需對以上代碼進行簡單改造即可,
- 給具體組件類 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");
}
}
- 給具體裝飾器類 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");
}
}
- 編寫 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】每周分享技術乾貨、開源項目、實戰經驗、國外優質文章翻譯等,您的關註將是我的更新動力!