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;
    }
}
相关推荐
胖咕噜的稞达鸭16 分钟前
自定义shell命令行解释器自制
java·开发语言
草莓熊Lotso16 分钟前
Git 分支管理:从基础操作到协作流程(本地篇)
大数据·服务器·开发语言·c++·人工智能·git·sql
报错小能手19 分钟前
C++异常处理 终极及总结
开发语言·c++
q***33373 小时前
oracle 12c查看执行过的sql及当前正在执行的sql
java·sql·oracle
tobebetter95276 小时前
How to manage python versions on windows
开发语言·windows·python
在未来等你6 小时前
AI Agent设计模式 Day 19:Feedback-Loop模式:反馈循环与自我优化
设计模式·llm·react·ai agent·plan-and-execute
Y***h1877 小时前
第二章 Spring中的Bean
java·后端·spring
9***P3347 小时前
PHP代码覆盖率
开发语言·php·代码覆盖率
8***29317 小时前
解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务(Spring MVC Springboot)同时允许跨域
java·前端·spring
CoderYanger7 小时前
优选算法-栈:67.基本计算器Ⅱ
java·开发语言·算法·leetcode·职场和发展·1024程序员节