2024-04-24 21:37:39
装饰器模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

Beverage:饮料抽象类,定义了饮料的基本属性和方法。
CondimentDecorator:调料装饰器抽象类,继承自Beverage,用于装饰Beverage对象。
DarkRoast:具体饮料类,表示一种特定的咖啡。
Mocha、Soy、Whip:具体调料装饰器类,用于为饮料添加特定的调料。
问题描述:
牛奶价钱上扬:需要修改所有包含牛奶调料的饮料成本计算。
新增焦糖调料:需要添加新的方法和修改成本计算逻辑。
违反原则:
开闭原则:类应该对扩展开放,对修改关闭。当前设计在添加新调料或修改调料价格时需要修改现有代码。
单一职责原则:一个类应该只有一个引起变化的原因。当前Beverage类不仅负责饮料本身,还负责调料的成本计算。
Beverage类:
public abstract class Beverage { String description = "Unknown Beverage"; public String getDescription() { return description; } public abstract double cost();}CondimentDecorator类:
public abstract class CondimentDecorator extends Beverage { public abstract String getDescription();}DarkRoast类:
public class DarkRoast extends Beverage { public DarkRoast() { description = "Dark Roast Coffee"; } public double cost() { return 0.99; }}Mocha类:
public class Mocha extends CondimentDecorator { Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } public String getDescription() { return beverage.getDescription() + ", Mocha"; } public double cost() { return 0.20 + beverage.cost(); }}调料价格变动:需要修改具体调料装饰器类中的成本计算逻辑。
新增调料:需要添加新的调料装饰器类,并可能修改Beverage类或相关逻辑。
新饮料类型:如果新饮料不支持某些调料,需要确保这些调料不会被错误地添加到新饮料上。
双倍调料:需要额外的逻辑来处理双倍或多倍调料的情况。
容量大小影响价格:需要在调料装饰器类中考虑饮料的容量大小。
开闭原则:类应该对扩展开放,对修改关闭。装饰器模式通过引入装饰器类来扩展功能,而不需要修改原有类的代码。
动态扩展功能:装饰器模式允许在运行时动态地添加或删除功能。
避免子类爆炸:相比继承,装饰器模式通过组合方式提供更灵活的功能扩展,避免了因功能组合而产生的大量子类。
保持接口一致性:装饰器和被装饰对象实现相同的接口,客户端可以一致地对待它们。
代码复杂度:引入大量装饰器类会增加系统的复杂度。
特定类型依赖:如果装饰器依赖于特定类型的对象,可能会在不经意间破坏系统的设计。
在CondimentDecorator中添加getSize()和setSize()方法:
public abstract class CondimentDecorator extends Beverage { Beverage beverage; public CondimentDecorator(Beverage beverage) { this.beverage = beverage; } public abstract String getDescription(); public Size getSize() { return beverage.getSize(); } public void setSize(Size size) { beverage.setSize(size); }}在具体调料装饰器中实现getSize()和cost()方法:
public class Soy extends CondimentDecorator { public Soy(Beverage beverage) { super(beverage); } public String getDescription() { return beverage.getDescription() + ", Soy"; } public double cost() { double cost = beverage.cost(); if (getSize() == Size.TALL) { cost += 0.10; } else if (getSize() == Size.GRANDE) { cost += 0.15; } else if (getSize() == Size.VENTI) { cost += 0.20; } return cost; }}继承的使用:装饰器模式通过继承来确保装饰器和被装饰对象具有相同的类型,但这也意味着当超类增加方法时,所有子类都需要相应改变。
与AOP的联系:装饰器模式的思想与面向切面编程(AOP)有相似之处,都是通过组合而非继承来动态添加功能。然而,AOP通常通过框架支持实现更复杂的横切关注点分离。
动态代理模式:在学习代理模式后,可以发现装饰器模式与动态代理模式有相似之处,都是通过包装对象来添加额外行为。但装饰器模式更侧重于功能的动态扩展,而代理模式则侧重于对对象的访问控制。