【设计模式系列笔记】装饰者模式

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 装饰者模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装类中来为原始对象添加新的行为。这种模式可以动态地将责任附加到对象上,而不影响其它对象。

1. 装饰者模式介绍

装饰者模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装类中来为原始对象添加新的行为。这种模式可以动态地将责任附加到对象上,而不影响其它对象。在Java中,装饰者模式通常涉及使用接口和抽象类来定义组件(被装饰的对象)和装饰者(用于包装组件的类)。

2. 关键思想

装饰者模式的关键思想是通过组合而不是继承的方式,动态地给一个对象添加一些额外的职责,同时又不改变其原有结构。这使得你可以在运行时选择性地、透明地、以任意顺序地添加功能,而不需要修改现有的代码。

以下是装饰者模式的一些关键思想:

  1. 接口或抽象类定义组件: 创建一个接口或抽象类,定义了被装饰者和装饰者共同的行为,确保它们都符合同一套接口。
  2. 具体组件实现接口: 创建具体的组件类,实现上述接口,提供原有的功能。
  3. 装饰者继承组件: 创建一个装饰者抽象类,它也实现了组件的接口,并包含一个指向组件的引用。这个引用允许装饰者包含一个或多个其他装饰者或具体组件。
  4. 具体装饰者扩展装饰者: 创建具体的装饰者类,它继承自装饰者抽象类。这些类可以添加额外的行为或修改原有行为。
  5. 运行时动态组合: 在运行时,通过将具体组件对象传递给具体装饰者的构造函数,可以动态地组合对象。由于装饰者和组件都实现了相同的接口,客户端代码对于组件和装饰者是透明的。
  6. 透明性: 客户端无需知道被装饰者和具体装饰者的具体类,只需知道它们实现了相同的接口或抽象类。这种透明性是装饰者模式的一个关键特点。

通过这种方式,装饰者模式允许你在不改变现有代码的情况下,动态地扩展对象的行为。这样的设计符合开放封闭原则,使得系统更加灵活和可维护。

3. 实现方式:

通过一个简单的咖啡店的例子来演示装饰者模式的实现。我们将有一个基本的咖啡组件,然后通过装饰者来添加不同的调料(装饰)。

// 咖啡组件接口
public interface Coffee {
    double cost(); // 获取咖啡的价格
}
// 咖啡装饰者抽象类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee; // 被装饰的咖啡对象
    // 构造方法接收被装饰的咖啡对象
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    // 装饰者抽象类实现了咖啡接口中的cost方法
    @Override
    public double cost() {
        return decoratedCoffee.cost(); // 保留原始咖啡的价格
    }
}
// 基本咖啡类
public class SimpleCoffee implements Coffee {
    @Override
    public double cost() {
        return 2.0; // 简单咖啡的价格
    }
}
// 牛奶装饰者类
public class MilkDecorator extends CoffeeDecorator {
    // 构造方法接收被装饰的咖啡对象
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    // 添加了牛奶的价格
    @Override
    public double cost() {
        return super.cost() + 1.0;
    }
}
// 糖装饰者类
public class SugarDecorator extends CoffeeDecorator {
    // 构造方法接收被装饰的咖啡对象
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }
    // 添加了糖的价格
    @Override
    public double cost() {
        return super.cost() + 0.5;
    }
}
// 客户端代码
public class CoffeeShop {
    public static void main(String[] args) {
        // 制作一杯简单咖啡
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println("Cost of Simple Coffee: $" + simpleCoffee.cost());
        // 使用装饰者添加牛奶
        Coffee milkCoffee = new MilkDecorator(simpleCoffee);
        System.out.println("Cost of Milk Coffee: $" + milkCoffee.cost());
        // 使用装饰者同时添加牛奶和糖
        Coffee milkSugarCoffee = new SugarDecorator(new MilkDecorator(simpleCoffee));
        System.out.println("Cost of Milk and Sugar Coffee: $" + milkSugarCoffee.cost());
    }
}

要点:

  1. 灵活的组合: 装饰者模式允许你通过组合的方式,动态地添加或覆盖对象的行为,使得系统更加灵活和可扩展。
  2. 保持接口一致性: 装饰者和被装饰者应该实现相同的接口或继承相同的抽象类,以保持一致性。这样客户端代码就可以使用装饰者和被装饰者的对象,而不需要关心具体的实现。
  3. 透明性: 客户端代码对于装饰者和被装饰者的行为应该是透明的。即客户端在不知道具体装饰者的情况下,仍然能够使用它们。
  4. 遵循开闭原则: 装饰者模式允许在运行时添加新的装饰者,而无需修改现有的代码。这符合开放封闭原则,即对扩展开放,对修改封闭。
  5. 多层嵌套: 可以通过多层嵌套装饰者,实现更复杂的组合行为。每个装饰者可以添加一些特定的行为。

注意事项:

  1. 过多的装饰者: 当装饰者层级过多时,可能会导致代码变得复杂且难以维护。需要慎重设计装饰者的数量,确保每个装饰者的责任清晰。
  2. 不同装饰者的顺序: 装饰者的顺序可能会影响最终的行为。需要注意装饰者的添加顺序,以确保得到期望的结果。
  3. 装饰者与组件的区分: 装饰者和组件应该有清晰的责任分工,装饰者负责添加额外的行为,而组件负责基本的功能。确保装饰者不包含与具体组件相关的业务逻辑。
  4. 性能影响: 装饰者模式可能会引入一些额外的开销,因为每个装饰者都会对操作进行一些处理。在性能敏感的情况下,需要仔细考虑。

总体而言,装饰者模式是一个非常强大的模式,可以用于动态地扩展对象的行为。在设计时需要权衡灵活性和复杂性,确保使用该模式的场景合适。

优点:

  1. 灵活性和可扩展性: 装饰者模式允许动态地、透明地向对象添加新的职责,而无需修改其代码。这提供了更大的灵活性和可扩展性。
  2. 遵循开闭原则: 装饰者模式符合开闭原则,允许在运行时添加新的装饰者,而无需修改现有代码。
  3. 透明性: 客户端无需知道具体装饰者和被装饰者的细节,可以像使用普通对象一样使用装饰后的对象。
  4. 单一职责原则: 每个具体的装饰者类都专注于添加一种特定的职责,保持了单一职责原则。

缺点:

  1. 复杂性: 随着装饰者层级的增加,可能会导致类的数量增加,使得代码变得复杂和难以维护。
  2. 顺序敏感: 装饰者的添加顺序可能影响最终的行为,需要注意装饰者的组合顺序。
  3. 性能开销: 装饰者模式可能引入一些额外的开销,因为每个装饰者都对操作进行一些处理。

应用场景:

  1. 动态地添加或覆盖行为: 当需要在不修改现有代码的情况下,动态地添加新的行为或覆盖现有行为时,装饰者模式是一种非常有用的设计模式。
  2. 避免使用子类进行扩展: 当类的继承体系变得庞大,且不希望通过创建大量的子类来扩展功能时,装饰者模式提供了一种更灵活的替代方案。
  3. 组合行为: 当需要组合多个不同的行为时,装饰者模式提供了一种通过不同组合来实现不同行为的方式。
  4. 透明性要求: 当希望对客户端代码保持透明性,使其无需知道具体对象的细节时,可以使用装饰者模式。
  5. 防止类爆炸: 当存在大量可能组合的特性时,通过装饰者模式可以避免创建大量的子类,从而避免类爆炸问题。
目录
相关文章
|
8天前
|
设计模式 Java
【设计模式系列笔记】责任链模式
责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它。每个处理者都有一个对下一个处理者的引用。责任链模式常用于处理请求的场景,例如在一个请求需要经过多个步骤或者多个对象来处理的情况下。
23 0
|
8天前
|
设计模式 缓存 监控
【设计模式系列笔记】代理模式
代理模式是一种结构型设计模式,它允许一个对象(代理对象)控制另一个对象的访问。代理对象通常充当客户端和实际对象之间的中介,用于对实际对象的访问进行控制、监控或其他目的。
43 1
|
8天前
|
设计模式
【设计模式】张一鸣笔记:责任链接模式怎么用?
【设计模式】张一鸣笔记:责任链接模式怎么用?
12 1
|
8天前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
26 4
|
8天前
|
设计模式 JavaScript 前端开发
vue的设计模式_笔记
vue的设计模式_笔记
16 0
|
8天前
|
设计模式 算法 编译器
【设计模式系列笔记】访问者模式
访问者模式是一种行为设计模式,旨在将算法与对象结构分离,使得能够在不修改元素类的前提下定义新的操作。这一模式的核心思想是在元素类中添加一个接受访问者的方法,从而实现在不同元素上执行不同操作的能力。
33 0
|
8天前
|
设计模式 SQL 算法
【设计模式系列笔记】模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的骨架,并允许子类在不改变该算法结构的情况下重新定义算法的某些步骤。这种模式属于行为型模式,它通过将算法的不同部分封装在不同的方法中,从而使子类能够在不改变算法结构的前提下定制算法的某些步骤。
33 0
|
8天前
|
设计模式 算法 Java
【设计模式系列笔记】策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。
27 0
|
8天前
|
设计模式 Java
【设计模式系列笔记】状态模式
在Java中,状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的关键思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态的对象。这样,当对象的状态发生变化时,其行为也会相应地发生变化。
25 0
|
8天前
|
设计模式 消息中间件 存储
【设计模式系列笔记】观察者模式
观察者模式是一种设计模式,它允许一个对象(主题)维护一组依赖于它的对象(观察者)并在状态发生变化时通知它们。这种模式属于行为型模式。在观察者模式中,主题是被观察的对象,而观察者是依赖于主题的对象,当主题的状态发生变化时,所有注册的观察者都会得到通知并自动更新。
24 0