Java设计模式之装饰者模式

问题

在某些情况下,我们需要在不修改现有对象结构的情况下,动态地添加功能或责任。继承在这种情况下可能会导致类爆炸问题,而且修改现有类可能会影响到其他部分的代码。

解决方案

装饰模式提供了一种在运行时动态地为对象添加新功能的方法,通过创建一个装饰类来包装原始类。装饰类具有与原始类相同的接口,它内部包含一个指向原始对象的引用,并且可以根据需要包装额外的功能。这样,你可以通过组合不同的装饰类来构建出具有不同功能组合的对象。

效果

装饰模式的优点包括避免了类爆炸问题,因为你可以通过组合少量的装饰类来实现各种功能组合。它也使得功能的增加和修改更加灵活,不会影响到其他部分的代码。然而,装饰模式可能会导致增加很多小型的类,从而增加了代码的复杂性。

在装饰模式中,通常涉及以下角色:

  1. 组件(Component):定义了一个抽象的接口,可以是具体对象或装饰器所共有的接口。
  2. 具体组件(Concrete Component):实现了组件接口,是被装饰的原始对象。
  3. 装饰器(Decorator):持有一个指向组件对象的引用,并实现了组件的接口。它可以包含额外的功能,也可以将请求传递给组件对象。
  4. 具体装饰器(Concrete Decorator):扩展了装饰器类,通过添加额外的功能来装饰具体组件。

通过这种方式,装饰模式允许你将功能嵌套地堆叠在一起,以实现各种不同的功能组合,同时保持代码的灵活性和可维护性。


动态的给一个对象添加一些额外的功能。就扩展功能而言,装饰者模式比生成子类方式更为灵活。

装饰器模式是一种结构性设计模式,它允许您在不影响同一类的其他对象的行为的情况下,静态或动态地向单个对象添加行为。 当您想要在运行时添加或删除对象的功能时,或者当您想要减少创建不同行为组合所需的子类数量时,此模式非常有用。

在Java中,使用继承和组合的结合来实现装饰器模式。 具体来说,您需要创建一个基类或接口来定义对象的核心行为,然后创建一个或多个装饰器类来向对象添加附加行为。 每个装饰器类都具有对其装饰的对象的引用,并且它可以在委托给对象的原始行为之前或之后修改对象的行为。

装饰器模式适用于以下场景:

  1. 在不修改现有代码的情况下,向现有类添加新的功能。
  2. 在运行时动态地向对象添加新的行为。
  3. 以不同的方式组合对象,以实现不同的行为。

使用装饰器模式时需要注意以下几点:

  1. 装饰器类需要实现与被装饰对象相同的接口,以便可以对被装饰对象进行包装。
  2. 装饰器类应该在调用被装饰对象的方法之前或之后添加新的行为。
  3. 不要创建过多的装饰器对象,否则会导致代码变得复杂难以维护。
java 复制代码
public interface Pizza {
    public String getDescription();
    public double getCost();
}
 // 具体组件
public class PlainPizza implements Pizza {
    public String getDescription() {
        return "薄饼";
    }
    public double getCost() {
        return 4.00;
    }
}
 // 装饰器
public abstract class ToppingDecorator implements Pizza {
    protected Pizza pizza;
    public ToppingDecorator(Pizza pizza) {
        this.pizza = pizza;
    }
    public String getDescription() {
        return pizza.getDescription();
    }
    public double getCost() {
        return pizza.getCost();
    }
}
 // 具体装饰器
public class Cheese extends ToppingDecorator {
    public Cheese(Pizza pizza) {
        super(pizza);
    }
    public String getDescription() {
        return pizza.getDescription() + ",马苏里拉奶酪";
    }
    public double getCost() {
        return pizza.getCost() + 0.50;
    }
}
 // 具体装饰器
public class Pepperoni extends ToppingDecorator {
    public Pepperoni(Pizza pizza) {
        super(pizza);
    }
    public String getDescription() {
        return pizza.getDescription() + ",意大利辣香肠";
    }
    public double getCost() {
        return pizza.getCost() + 1.00;
    }
}
 // 客户端代码
public class PizzaShop {
    public static void main(String[] args) {
        Pizza pizza = new PlainPizza();
        pizza = new Cheese(pizza);
        pizza = new Pepperoni(pizza);
        System.out.println(pizza.getDescription());
        System.out.println("成本:$" + pizza.getCost());
    }
}

实际应用场景

比如为商品添加不同的属性或功能,如包装、赠品等。

java 复制代码
interface ProductComponent {
    double getPrice();
}

class BasicProduct implements ProductComponent {
    @Override
    public double getPrice() {
        return 50.0;
    }
}

class GiftDecorator extends ProductComponent {
    private ProductComponent component;

    public GiftDecorator(ProductComponent component) {
        this.component = component;
    }

    @Override
    public double getPrice() {
        return component.getPrice() + 10.0;
    }
}
相关推荐
卢锡荣20 分钟前
乐得瑞Type-c接口产品描述
c语言·开发语言
晴天的雨.99220 分钟前
【C++】模板进阶
开发语言·c++·算法
倔强的石头10624 分钟前
【Linux指南】进程通信系列(二):匿名管道 —— pipe 的本质、fork 继承与四种读写规则
java·linux·运维
调试优选官29 分钟前
2026年IoT物联网开发赛道年度榜单深度解析:D-coding物联网系统定制与源码交付能力专项解读
java·物联网·struts·iot·开发经验
木井巳31 分钟前
【动态规划】三步问题
java·算法·leetcode·动态规划·推荐算法
小张成长计划..34 分钟前
【Linux】25:线程概念和控制
java·linux·jvm
蛋蛋的就会顺顺的9 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
需要82610 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
小 黄 鸡10 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
死磕音视频11 小时前
让小模型做选择题:限定候选项并读取 logits 概率的入门实践(附 Python 代码)
开发语言·python·大模型·文本分类·transformers