设计模式-装饰模式
装饰模式(Decorator Pattern) 是结构型设计模式之一,它允许动态地给一个对象添加一些额外的职责,而无需改变其结构。装饰模式通过创建一个装饰类,使用继承的方式来扩展对象的功能,从而避免了大量的子类化问题。
装饰模式的关键要点:
- 组件接口(Component):定义一个接口,表示被装饰的对象和装饰器的共同接口。
- 具体组件类(ConcreteComponent):实现组件接口,表示被装饰的具体对象。
- 装饰器类(Decorator):持有一个
Component
对象的引用,并通过委托给该对象来扩展其功能。 - 具体装饰器类(ConcreteDecorator):继承自装饰器类,并添加额外的功能。
适用场景:
- 需要扩展对象的功能,但又不想通过继承的方式增加大量的子类。
- 动态地给一个对象添加职责,而不影响其他对象。
- 需要为多个对象添加相同功能时,避免在每个类中都实现重复代码。
具体步骤:
- 定义一个组件接口,它可以是一个抽象类或接口。
- 创建一个具体组件类,实现该接口或抽象类,代表基本功能。
- 创建装饰器类,持有一个组件对象并扩展其功能。
- 创建多个具体装饰器类,给对象添加具体的附加功能。
代码示例
假设我们有一个饮料类,饮料可以有不同的配料(如牛奶、糖、巧克力等)。我们希望通过装饰模式来动态添加这些配料,而无需修改原始的饮料类。
1. 定义组件接口
// 组件接口
public interface Beverage {
double cost(); // 计算饮料的费用
String description(); // 获取饮料的描述
}
2. 创建具体组件类
// 具体组件:基础饮料
public class Coffee implements Beverage {
@Override
public double cost() {
return 5.0; // 咖啡的基本价格
}
@Override
public String description() {
return "Coffee"; // 饮料的描述
}
}
3. 创建装饰器类
// 装饰器类,持有一个 Beverage 对象
public abstract class BeverageDecorator implements Beverage {
protected Beverage beverage; // 持有一个 Beverage 对象的引用
public BeverageDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost(); // 委托给 beverage 的 cost 方法
}
@Override
public String description() {
return beverage.description(); // 委托给 beverage 的 description 方法
}
}
4. 创建具体装饰器类
// 具体装饰器1:添加牛奶
public class MilkDecorator extends BeverageDecorator {
public MilkDecorator(Beverage beverage) {
super(beverage); // 构造器中传入 Beverage 对象
}
@Override
public double cost() {
return beverage.cost() + 1.5; // 牛奶的附加费用
}
@Override
public String description() {
return beverage.description() + " with Milk"; // 饮料描述中加上“牛奶”
}
}
// 具体装饰器2:添加糖
public class SugarDecorator extends BeverageDecorator {
public SugarDecorator(Beverage beverage) {
super(beverage); // 构造器中传入 Beverage 对象
}
@Override
public double cost() {
return beverage.cost() + 0.5; // 糖的附加费用
}
@Override
public String description() {
return beverage.description() + " with Sugar"; // 饮料描述中加上“糖”
}
}
5. 客户端代码使用装饰模式
public class Main {
public static void main(String[] args) {
// 创建一杯咖啡
Beverage beverage = new Coffee();
// 给咖啡加牛奶
beverage = new MilkDecorator(beverage);
// 给咖啡加糖
beverage = new SugarDecorator(beverage);
// 打印饮料的描述和价格
System.out.println("Description: " + beverage.description()); // Coffee with Milk with Sugar
System.out.println("Cost: " + beverage.cost()); // 7.0
}
}
输出:
Description: Coffee with Milk with Sugar
Cost: 7.0
解释:
-
Beverage 接口 定义了所有饮料(包括装饰器和具体饮料类)的共同行为。它有两个方法:
cost()
用来计算饮料的费用。description()
用来描述饮料。
-
Coffee 类 实现了
Beverage
接口,表示基础饮料,计算基础费用并返回描述。 -
BeverageDecorator 类 是一个抽象装饰器类,持有一个
Beverage
对象的引用,并通过委托的方式调用Beverage
的方法。具体的装饰器类继承自这个类,并扩展其功能。 -
MilkDecorator 和 SugarDecorator 类 是具体装饰器类,它们通过装饰原始饮料对象来增加功能(例如,添加牛奶和糖),并修改其
cost()
和description()
方法。 -
客户端代码 创建了一个
Coffee
对象,并使用装饰器动态添加了牛奶和糖,最后打印出饮料的描述和价格。
优点:
- 增强对象的功能:装饰模式能够在不修改类的情况下增强对象的功能。
- 避免类爆炸:通过组合多个装饰器来实现不同的功能,而不是通过继承创建大量的子类。
- 可以动态改变对象的行为:装饰模式可以在运行时动态地给对象增加行为,而不需要修改对象的原始代码。
缺点:
- 装饰器的数量过多时会变得复杂:如果装饰器过多,使用时会变得复杂,因为需要组合多个装饰器来完成任务。
- 可能会导致过多的类:每增加一个新的装饰功能,就需要增加一个装饰器类,可能会导致类的数量急剧增加。
总结:
装饰模式提供了一种灵活的方式来为对象动态地添加新功能。它可以有效避免子类化带来的问题,支持对象行为的动态改变,并且在许多情况下优于继承。当你需要扩展对象的功能并且希望能动态地添加功能时,装饰模式是一个非常合适的选择。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体