首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 裝飾器模式深度解析:從 I/O 流到漢堡製作的實戰演示

     編輯:Java 相關知識

模式定義與設計初衷

裝飾器模式:動態地給物件新增職責,如同牆面刷漆層層疊加。相較於繼承產生大量子類別,裝飾器提供更靈活的擴展方式。

核心痛點

  • 繼承導致類別爆炸:每種功能組合都需新建子類
  • 繼承為靜態編譯期決定,無法運行時動態組合
  • 違反開閉原則:新增功能需修改既有類別層級

關鍵設計原則

  • 裝飾者與被裝飾者共享超類型:繼承的是類型,非行為
  • 組合大於繼承:透過持有被裝飾者引用實現功能疊加
  • Java I/O 為經典實例:BufferedReader(new FileReader(file)) 即裝飾器鏈

案例一:人員穿衣與導航的多層裝飾

核心介面與抽象裝飾者

// 共同超類型
public interface Human {
    void wearClothes();
    void walkToWhere();
}

// 抽象裝飾者:持有被裝飾者,預設委派
public abstract class Decorator implements Human {
    protected Human human;
    
    public Decorator(Human human) { this.human = human; }
    
    @Override public void wearClothes() { human.wearClothes(); }
    @Override public void walkToWhere() { human.walkToWhere(); }
}

具體裝飾者層層疊加

// 第零層:進房子、找地圖
public class DecoratorZero extends Decorator {
    public DecoratorZero(Human human) { super(human); }
    private void goHome() { System.out.println("進房子。。"); }
    private void findMap() { System.out.println("書房找找Map。。"); }
    @Override public void wearClothes() { super.wearClothes(); goHome(); }
    @Override public void walkToWhere() { super.walkToWhere(); findMap(); }
}

// 第一層:去衣櫃、在地圖找地點
public class DecoratorFirst extends Decorator {
    public DecoratorFirst(Human human) { super(human); }
    private void goClothespress() { System.out.println("去衣櫃找找看。。"); }
    private void findPlaceOnMap() { System.out.println("在Map上找找。。"); }
    @Override public void wearClothes() { super.wearClothes(); goClothespress(); }
    @Override public void walkToWhere() { super.walkToWhere(); findPlaceOnMap(); }
}

// 第二層:找到衣服、找到目標地點
public class DecoratorTwo extends Decorator {
    public DecoratorTwo(Human human) { super(human); }
    private void findClothes() { System.out.println("找到一件D&G。。"); }
    private void findTheTarget() { System.out.println("在Map上找到神秘花園和城堡。。"); }
    @Override public void wearClothes() { super.wearClothes(); findClothes(); }
    @Override public void walkToWhere() { super.walkToWhere(); findTheTarget(); }
}

被裝飾者基礎實現

public class Person implements Human {
    @Override public void wearClothes() { System.out.println("穿什麼呢。。"); }
    @Override public void walkToWhere() { System.out.println("去哪裡呢。。"); }
}

測試驗證:鏈式調用順序

public class Test {
    public static void main(String[] args) {
        Human person = new Person();
        // 由內而外包裝:Person → Zero → First → Two
        Decorator decorator = new DecoratorTwo(
            new DecoratorFirst(new DecoratorZero(person))
        );
        decorator.wearClothes();
        decorator.walkToWhere();
    }
}

輸出結果分析

穿什麼呢。。          // Person 基礎行為
進房子。。            // Zero 新增
去衣櫃找找看。。      // First 新增
找到一件D&G。。       // Two 新增
去哪裡呢。。          // Person 基礎行為
書房找找Map。。       // Zero 新增
在Map上找找。。       // First 新增
在Map上找到神秘花園和城堡。。 // Two 新增

執行順序鐵律:每層 super.method() 先執行內層邏輯,再執行自身新增邏輯,形成「由內向外」的洋蔥模型。

案例二:漢堡配料動態組合(更貼近業務場景)

類別結構對應

角色漢堡案例人員案例
ComponentHumburger (抽象)Human (介面)
ConcreteComponentChickenBurgerPerson
DecoratorCondiment (抽象)Decorator (抽象類)
ConcreteDecoratorLettuce, ChilliDecoratorZero, First, Two

代碼實現


// 漢堡基類
public abstract class Humburger {
    protected String name;
    public String getName() { return name; }
    public abstract double getPrice();
}

// 基礎漢堡
public class ChickenBurger extends Humburger {
    public ChickenBurger() { name = "雞腿堡"; }
    @Override public double getPrice() { return 10; }
}

// 配料抽象裝飾者
public abstract class Condiment extends Humburger {
    public abstract String getName();
}

// 生菜裝飾
public class Lettuce extends Condiment {
    private Humburger hamburger;
    public Lettuce(Humburger hamburger) { this.hamburger = hamburger; }
    @Override public String getName() { return hamburger.getName() + " 加生菜"; }
    @Override public double getPrice() { return hamburger.getPrice() + 1.5; }
}

// 辣椒裝飾(免費)
public class Chilli extends Condiment {
    private Humburger hamburger;
    public Chilli(Humburger hamburger) { this.hamburger = hamburger; }
    @Override public String getName() { return hamburger.getName() + " 加辣椒"; }
    @Override public double getPrice() { return hamburger.getPrice(); }
}

靈活組合測試

public class Test {
    public static void main(String[] args) {
        Humburger burger = new ChickenBurger();
        System.out.println(burger.getName() + " 價錢:" + burger.getPrice());
        // 雞腿堡 價錢:10.0

        Humburger withLettuce = new Lettuce(burger);
        System.out.println(withLettuce.getName() + " 價錢:" + withLettuce.getPrice());
        // 雞腿堡 加生菜 價錢:11.5

        Humburger withChilli = new Chilli(burger);
        System.out.println(withChilli.getName() + " 價錢:" + withChilli.getPrice());
        // 雞腿堡 加辣椒 價錢:10.0

        Humburger combo = new Chilli(new Lettuce(burger));
        System.out.println(combo.getName() + " 價錢:" + combo.getPrice());
        // 雞腿堡 加生菜 加辣椒 價錢:11.5
    }
}

核心設計原則深度剖析

1. 委派機制

抽象裝飾者持有 Component 引用,所有操作預設委派給被裝飾者,具體裝飾者選擇性擴展特定方法。

2. 依賴倒置體現

Decorator dt = new DecoratorTwo(new DecoratorFirst(new DecoratorZero(human))) 中,抽象類 Decorator 依賴具體實現(由建構子注入),而非反過來。

3. 開閉原則

新增裝飾行為只需新增 Decorator 子類,無須修改既有類別。擴展發生在子類,修改關閉在抽象層。

4. 單一職責

每個裝飾者只負責一項具體功能擴展(如價格計算、名稱拼接、行為新增)。

實務應用場景

  • Java I/O 流:new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))
  • Spring 中的 BeanPostProcessor:後置處理器鏈式裝飾 Bean
  • HTTP 請求/回應攔截器:日誌、壓縮、加密、快取層層包裝
  • 圖形介面元件:邊框、滾動條、陰影動態組合

常見誤區與最佳實踐

誤區正確做法
裝飾者修改被裝飾者狀態僅新增行為,不破壞原有語義
過度使用導致層級過深層級超過 3-4 層考慮重構或策略模式
混淆裝飾器與代理模式代理控制存取,裝飾器新增職責

總結

裝飾器模式以「組合替代繼承」,實現運行時動態擴展物件功能。掌握其核心——共同超類型、持有被裝飾者引用、委派+選擇性擴展,即可靈活應對各類功能疊加場景。Java I/O 體系是最佳學習範本,建議親手追蹤源碼體會洋蔥模型的優雅。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved