设计模式- 装饰器模式(Decorator Pattern)结构|原理|优缺点|场景|示例

设计模式(分类) 设计模式(六大原则)

创建型(5种) 工厂方法 抽象工厂模式 单例模式 建造者模式 原型模式

结构型(7种) 适配器模式 装饰器模式 代理模式 ​​​​​​外观模式 桥接模式 组合模式 享元模式

行为型(11种) 策略模式 模板方法模式 观察者模式 迭代器模式 责任链模式 命令模式

备忘录模式 状态模式 访问者模式 中介者模式


装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给对象添加新的职责(功能)或改变其原有行为。装饰器模式通过创建一个装饰器类,该类包装(持有)原始对象,并在保持原始对象接口不变的前提下,通过代理或继承的方式添加新的功能。装饰器模式可以提供比继承更灵活的扩展方式,因为它可以在不修改原有类的情况下,为对象添加新功能,并且可以叠加多个装饰器以实现多重增强。

模式结构

装饰器模式通常包含以下角色:

  1. 组件接口(Component):定义了基础功能的接口,所有具体组件和装饰器都要实现这个接口,以便于保持接口的一致性。

  2. 具体组件(Concrete Component):实现了组件接口,是需要被装饰的基础对象。

  3. 装饰器(Decorator):实现了组件接口,并持有一个组件接口类型的引用,用于存储被装饰的对象。装饰器可以在实现组件接口方法的基础上,添加新的职责或修改原有行为。

  4. 具体装饰器(Concrete Decorators):继承自装饰器类,为具体组件添加新的职责或修改其行为。具体装饰器可以在构造函数中接收被装饰的对象,并在实现组件接口方法时调用被装饰对象的对应方法。

工作原理

  • 客户端:创建具体组件对象,并根据需要通过装饰器对其进行装饰。客户端始终面向组件接口编程,无需关心对象是否被装饰以及如何装饰。
  • 装饰器:持有一个组件接口类型的引用,用于存储被装饰的对象。装饰器在实现组件接口方法时,可以调用被装饰对象的对应方法,并在此基础上添加新功能或修改原有行为。
  • 具体装饰器:在构造函数中接收被装饰的对象,并在实现组件接口方法时调用被装饰对象的对应方法,同时添加新的职责或修改原有行为。

优缺点

优点
  • 开放封闭原则:装饰器模式遵循"开闭原则",允许在不修改原有类的情况下,动态地为对象添加新功能,增强了系统的可扩展性。
  • 灵活性:可以使用多个装饰器对同一个对象进行多次装饰,从而实现不同组合的增强功能。
  • 透明性:装饰后的对象与未装饰的对象在对外接口上保持一致,客户端无需关心对象是否被装饰以及如何装饰,只需面向组件接口编程。
缺点
  • 过度使用可能导致类层次过深:如果过度使用装饰器模式,可能会导致类的层次过深,增加系统的复杂性。
  • 不易于理解:对于不熟悉装饰器模式的开发人员来说,可能需要花费更多时间理解装饰器的实现逻辑。

适用场景

  • 需要为对象动态添加新功能,且新功能与原有功能可独立变化:装饰器模式可以在运行时为对象添加新功能,新功能与原有功能通过装饰器类进行封装,二者可以独立变化。
  • 需要保持类的开放封闭原则,避免修改已有代码:装饰器模式可以在不修改原有类的情况下,通过创建装饰器类为对象添加新功能,符合开放封闭原则。
  • 需要为对象提供多种可选的附加功能,且这些功能可以自由组合:通过使用多个装饰器对同一对象进行装饰,可以实现不同组合的增强功能。

代码示例(以Java为例)

java 复制代码
// 组件接口
interface Coffee {
    double getCost();
    String getDescription();
}

// 具体组件
class SimpleCoffee implements Coffee {
    @Override
    public double getCost() {
        return 10.0;
    }

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

// 装饰器
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

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

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }

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

// 具体装饰器
class MilkCoffee extends CoffeeDecorator {
    public MilkCoffee(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public double getCost() {
        return super.getCost() + 2.0;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", with milk";
    }
}

class VanillaCoffee extends CoffeeDecorator {
    public VanillaCoffee(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public double getCost() {
        return super.getCost() + 3.0;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", with vanilla";
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println(simpleCoffee.getDescription() + " costs " + simpleCoffee.getCost());  
        // 输出:Simple coffee costs 10.0

        Coffee milkCoffee = new MilkCoffee(simpleCoffee);
        System.out.println(milkCoffee.getDescription() + " costs " + milkCoffee.getCost());  
        // 输出:Simple coffee, with milk costs 12.0

        Coffee vanillaMilkCoffee = new VanillaCoffee(milkCoffee);
        System.out.println(vanillaMilkCoffee.getDescription() + " costs " + vanillaMilkCoffee.getCost());  
        // 输出:Simple coffee, with milk, with vanilla costs 15.0
    }
}

在这个Java示例中:

  • Coffee接口作为组件接口,定义了获取咖啡价格和描述的方法。
  • SimpleCoffee类是需要被装饰的具体组件,实现了基础的咖啡功能。
  • CoffeeDecorator类作为装饰器,实现了Coffee接口,并持有一个Coffee类型的引用,用于存储被装饰的对象。装饰器类实现了getCost()getDescription()方法,但并未添加新功能,主要是为了方便具体装饰器类的继承。
  • MilkCoffeeVanillaCoffee类是具体装饰器,分别继承自CoffeeDecorator,并在构造函数中接收被装饰的对象。它们在实现getCost()getDescription()方法时,调用了被装饰对象的对应方法,并在此基础上添加了新功能(牛奶和香草)的费用和描述。
  • 客户端代码创建SimpleCoffee对象,并根据需要通过装饰器对其进行装饰,最终得到添加了牛奶和香草的咖啡。客户端始终面向Coffee接口编程,无需关心对象是否被装饰以及如何装饰。

代码示例(以Python为例)

python 复制代码
# 组件接口
class Coffee:
    def get_cost(self):
        raise NotImplementedError("Subclasses must implement this method")

    def get_description(self):
        raise NotImplementedError("Subclasses must implement this method")

# 具体组件
class SimpleCoffee(Coffee):
    def __init__(self):
        self.cost = 10
        self.description = "Simple coffee"

    def get_cost(self):
        return self.cost

    def get_description(self):
        return self.description

# 装饰器
class CoffeeDecorator(Coffee):
    def __init__(self, decorated_coffee: Coffee):
        self.decorated_coffee = decorated_coffee

    def get_cost(self):
        return self.decorated_coffee.get_cost()

    def get_description(self):
        return self.decorated_coffee.get_description()

# 具体装饰器
class MilkCoffee(CoffeeDecorator):
    def __init__(self, decorated_coffee: Coffee):
        super().__init__(decorated_coffee)
        self.cost = decorated_coffee.get_cost() + 2
        self.description = decorated_coffee.get_description() + ", with milk"

class VanillaCoffee(CoffeeDecorator):
    def __init__(self, decorated_coffee: Coffee):
        super().__init__(decorated_coffee)
        self.cost = decorated_coffee.get_cost() + 3
        self.description = decorated_coffee.get_description() + ", with vanilla"

# 客户端代码
def main():
    simple_coffee = SimpleCoffee()
    print(simple_coffee.get_description(), "costs", simple_coffee.get_cost())  # 输出:Simple coffee costs 10

    milk_coffee = MilkCoffee(simple_coffee)
    print(milk_coffee.get_description(), "costs", milk_coffee.get_cost())  # 输出:Simple coffee, with milk costs 12

    vanilla_milk_coffee = VanillaCoffee(milk_coffee)
    print(vanilla_milk_coffee.get_description(), "costs", vanilla_milk_coffee.get_cost())  # 输出:Simple coffee, with milk, with vanilla costs 15

if __name__ == "__main__":
    main()

在这个Python示例中:

  • Coffee类作为组件接口,定义了获取咖啡价格和描述的接口。
  • SimpleCoffee类是需要被装饰的具体组件,实现了基础的咖啡功能。
  • CoffeeDecorator类作为装饰器,实现了组件接口,并持有一个Coffee类型的引用,用于存储被装饰的对象。装饰器类实现了get_cost()get_description()方法,但并未添加新功能,主要是为了方便具体装饰器类的继承。
  • MilkCoffeeVanillaCoffee类是具体装饰器,分别继承自CoffeeDecorator,并在构造函数中接收被装饰的对象。它们在实现get_cost()get_description()方法时,调用了被装饰对象的对应方法,并在此基础上添加了新功能(牛奶和香草)的费用和描述。
  • 客户端代码创建SimpleCoffee对象,并根据需要通过装饰器对其进行装饰,最终得到添加了牛奶和香草的咖啡。客户端始终面向Coffee接口编程,无需关心对象是否被装饰以及如何装饰。
相关推荐
奋进的芋圆21 分钟前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin38 分钟前
设计模式之桥接模式
java·设计模式·桥接模式
model200538 分钟前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉1 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国1 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882481 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈2 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_992 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹2 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理
专注_每天进步一点点2 小时前
【java开发】写接口文档的札记
java·开发语言