「层层包装」—— 装饰器模式

文章目录

示例代码

代码示例

完整代码

bash 复制代码
src/
├── common/                          # 共享接口包
│   └── Coffee.java                 # Component:组件接口
│
└── decorator/                      # 装饰器实现
    ├── SimpleCoffee.java          # Concrete Component:具体组件
    ├── CoffeeDecorator.java       # Base Decorator:基础装饰器
    ├── MilkDecorator.java         # Concrete Decorator:牛奶装饰器
    ├── SugarDecorator.java        # Concrete Decorator:糖装饰器
    ├── WhipDecorator.java         # Concrete Decorator:奶泡装饰器
    ├── CoffeeShopClient.java      # Client:客户端
    └── DecoratorDemo.java         # 运行入口

编译运行

bash 复制代码
# 编译
javac -d out src/common/*.java src/decorator/*.java

# 运行
java -cp out decorator.DecoratorDemo

什么是装饰器模式?

概念

装饰器模式 是一种结构型设计模式,它的核心作用是动态地给对象添加一些额外的职责。装饰器让对象可以在运行时扩展功能,而不需要修改原有的类。

简单来说,装饰器就是在包装

生活类比

就像咖啡店的点单系统:

  • 原味咖啡有基础价格和描述
  • 可以加牛奶(+5元)
  • 可以加糖(+2元)
  • 可以加奶泡(+8元)
  • 各种配料可以自由组合

在代码世界中,装饰器模式干的是同样的事:

  • 基础组件提供了核心功能
  • 装饰器可以在运行时包装组件,添加新功能
  • 多个装饰器可以层层嵌套,组合出不同的效果

装饰器模式中的四个角色

Component(组件接口)

被装饰对象的接口。在我们的例子中是 Coffee

java 复制代码
// src/common/Coffee.java
package common;

public interface Coffee {
    int getCost();
    String getDescription();
}

客户端通过这个接口操作所有咖啡。

ConcreteComponent(具体组件)

实现组件接口的基础对象。在我们的例子中是 SimpleCoffee

java 复制代码
// src/decorator/SimpleCoffee.java
package decorator;

import common.Coffee;

public class SimpleCoffee implements Coffee {
    @Override
    public int getCost() {
        return 10;
    }

    @Override
    public String getDescription() {
        return "原味咖啡";
    }
}

这是最基础的咖啡,不带任何配料。

Decorator(装饰器)

持有一个组件引用,并实现组件接口。装饰器本身也是组件,所以可以递归嵌套:

java 复制代码
// src/decorator/CoffeeDecorator.java
package decorator;

import common.Coffee;

public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public int getCost() {
        return coffee.getCost();
    }

    @Override
    public String getDescription() {
        return coffee.getDescription();
    }
}

ConcreteDecorator(具体装饰器)

实际的装饰器实现,给组件添加新的行为:

java 复制代码
// src/decorator/MilkDecorator.java
package decorator;

import common.Coffee;

public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public int getCost() {
        return super.getCost() + 5;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " + 牛奶";
    }
}

装饰器模式示例

结构图

bash 复制代码
客户端 --调用--> Coffee (Component)
                    ↑
                    |
                    | implements
            CoffeeDecorator (Decorator)
                    |
                    | 组合
                    ↓
            SimpleCoffee (ConcreteComponent)

运行示例

bash 复制代码
>>> 点一杯原味咖啡
  点单: 原味咖啡
  价格: ¥10

>>> 原味咖啡 + 牛奶
  点单: 原味咖啡 + 牛奶
  价格: ¥15

>>> 原味咖啡 + 牛奶 + 糖
  点单: 原味咖啡 + 牛奶 + 糖
  价格: ¥17

>>> 原味咖啡 + 牛奶 + 糖 + 奶泡
  点单: 原味咖啡 + 牛奶 + 糖 + 奶泡
  价格: ¥25

代码实现

java 复制代码
// 装饰器可以层层嵌套
Coffee coffee = new WhipDecorator(
    new SugarDecorator(
        new MilkDecorator(
            new SimpleCoffee()
        )
    )
);
coffee.getDescription(); // "原味咖啡 + 牛奶 + 糖 + 奶泡"
coffee.getCost();       // 25

装饰器模式 vs 继承

特性 继承 装饰器
扩展时机 编译时 运行时
组合灵活性 静态的,不易改变 动态可组合,任意组合
类数量 每种组合都需要一个新类 只需组件类 + 装饰器类
开闭原则 违反(修改现有类) 符合(扩展新功能无需修改现有类)

装饰器模式的优势

  1. 运行时扩展:可以在程序运行期间动态添加装饰
  2. 自由组合:多种配料可以任意组合,牛奶+糖+奶泡
  3. 开闭原则:对扩展开放,对修改封闭
  4. 单一职责:每个装饰器只关心自己添加的那部分功能
  5. 可复用性:装饰器之间相互独立,可单独复用

装饰器模式的注意事项

  1. 装饰器链不要太长:嵌套层次过多会导致调试困难
  2. 顺序敏感:不同的添加顺序会产生不同的结果
  3. 类型检查:需要注意被装饰对象的真实类型
相关推荐
java1234_小锋1 小时前
String、StringBuilder、StringBuffer的区别?
java·开发语言
星原望野1 小时前
JAVA集合:List、Set和Map
java·开发语言·list·set·map·集合
摘星小杨2 小时前
如何在前端循环调取接口,实时查询数据
开发语言·前端·javascript
yujunl2 小时前
U9的UI插件客开的总结1
开发语言
多敲代码防脱发2 小时前
Spring进阶(容器实现)
java·开发语言·后端·spring
m0_702036532 小时前
mysql如何通过索引减少行锁范围_mysql索引与加锁逻辑
jvm·数据库·python
用户0332126663672 小时前
使用 Python 设置 Word 文档文本的颜色
python
qxwlcsdn2 小时前
如何用 IndexedDB 存储从 API 获取的超大列表并实现二级索引
jvm·数据库·python
小新同学^O^2 小时前
简单学习 --> 模型微调
开发语言·人工智能·python·模型微淘