定义:
装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。 就增加功能来说,装饰模式相比生成子类更为灵活。)

装饰模式通用类图
装饰设计模式的核心思想是组合优于继承。它不通过继承父类来获取新功能,而是将对象的功能扩展逻辑封装在装饰器类中,通过组合的方式将装饰器与被装饰对象关联起来。这样,在运行时可以灵活地为对象添加、移除或替换装饰器,实现功能的动态变化,使代码更加灵活和可维护。
角色:
装饰模式包含以下几个核心角色:
1、抽象组件(Component)
抽象组件定义了具体组件和装饰器的共同接口,它是被装饰对象和装饰器的抽象基类。客户端通过该接口与具体组件或被装饰后的组件进行交互,确保客户端可以一致地处理具体组件和装饰器。
2、具体组件(Concrete Component)
具体组件是抽象组件的具体实现,它是被装饰的原始对象,实现了抽象组件定义的基本功能。
3、抽象装饰器(Decorator)
抽象装饰器继承或实现了抽象组件接口,并且持有一个抽象组件类型的引用。它的主要作用是为具体装饰器提供一个通用的包装结构,在抽象装饰器中可以实现一些装饰器的通用行为,同时为具体装饰器扩展功能预留接口。
4、具体装饰器(Concrete Decorator)
具体装饰器是抽象装饰器的具体实现类,它负责为被装饰对象添加具体的功能。每个具体装饰器都可以根据自身的逻辑,在调用被装饰对象的方法前后,添加额外的行为,从而实现功能的增强。
代码示例:
下面通过一个饮品制作示例来展示装饰设计模式的实现。假设我们有基础的饮品(如咖啡、茶),同时可以为饮品添加各种配料(如牛奶、糖、奶油),每种配料都会增加饮品的价格和描述信息。
(一)定义抽象组件
// 饮品抽象组件
public interface Beverage {
String getDescription();
double cost();
}
(二)定义具体组件
// 具体饮品:咖啡
public class Coffee implements Beverage {
@Override
public String getDescription() {
return "咖啡";
}
@Override
public double cost() {
return 10.0;
}
}
// 具体饮品:茶
public class Tea implements Beverage {
@Override
public String getDescription() {
return "茶";
}
@Override
public double cost() {
return 8.0;
}
}
(三)定义抽象装饰器
// 饮品装饰器抽象类
public abstract class BeverageDecorator implements Beverage {
protected Beverage beverage;
public BeverageDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public abstract String getDescription();
@Override
public abstract double cost();
}
(四)定义具体装饰器
// 具体装饰器:牛奶
public class Milk extends BeverageDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 牛奶";
}
@Override
public double cost() {
return beverage.cost() + 2.0;
}
}
// 具体装饰器:糖
public class Sugar extends BeverageDecorator {
public Sugar(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 糖";
}
@Override
public double cost() {
return beverage.cost() + 1.0;
}
}
// 具体装饰器:奶油
public class Cream extends BeverageDecorator {
public Cream(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 奶油";
}
@Override
public double cost() {
return beverage.cost() + 3.0;
}
}
(五)客户端使用
public class DecoratorPatternClient {
public static void main(String[] args) {
// 创建一杯基础咖啡
Beverage coffee = new Coffee();
System.out.println(coffee.getDescription() + ",价格:" + coffee.cost() + "元");
// 为咖啡添加牛奶和糖
Beverage coffeeWithMilkAndSugar = new Sugar(new Milk(coffee));
System.out.println(coffeeWithMilkAndSugar.getDescription() + ",价格:" + coffeeWithMilkAndSugar.cost() + "元");
// 创建一杯基础茶,并添加奶油
Beverage tea = new Tea();
Beverage teaWithCream = new Cream(tea);
System.out.println(teaWithCream.getDescription() + ",价格:" + teaWithCream.cost() + "元");
}
}
优点 :
1、灵活扩展功能:可以在不修改原有对象代码的情况下,动态地为对象添加新功能,并且可以根据需要自由组合不同的装饰器,实现功能的多样化扩展。
2、遵循开闭原则:新增装饰器类不会影响到已有的代码,符合开闭原则,提高了代码的可维护性和可扩展性。
3、避免继承的局限性:相比于继承,装饰设计模式更加灵活,避免了因继承导致的类层次结构复杂、代码复用性差等问题。
缺点:
1、多层装饰会增加复杂度:当装饰器层数过多时,会导致代码结构变得复杂,调试和理解起来更加困难。例如,一个对象被多个装饰器层层包裹,追踪方法调用的顺序和逻辑会变得繁琐。
2、性能开销:每个装饰器都需要创建一个对象来包装被装饰对象,这在一定程度上会增加系统的内存开销和方法调用的时间开销。
使用场景:
(一)需要动态添加功能
当系统需要在运行时为对象动态添加、移除或替换功能,而不是在编译时通过继承来实现功能扩展时,装饰设计模式是一个很好的选择。比如在图形绘制系统中,为图形动态添加边框、阴影等效果。
(二)避免子类过多
如果使用继承方式来实现功能扩展,会导致子类数量急剧增加,使得代码难以维护。装饰设计模式通过组合装饰器的方式,将功能扩展逻辑分散到多个装饰器类中,避免了类的爆炸式增长。
(三)对同一对象进行多种组合功能增强
当需要对同一个对象进行多种不同的功能增强,并且这些功能可以以不同的组合方式应用时,装饰设计模式可以灵活地实现这一需求。例如在游戏角色系统中,为角色添加多种不同的技能组合。
装饰设计模式通过巧妙的组合方式,实现了对象功能的动态扩展,为软件开发中功能增强的需求提供了一种优雅且灵活的解决方案。它在避免子类泛滥、遵循开闭原则方面表现出色,适用于多种需要动态添加功能的场景。但在使用时,也要注意控制装饰器的层数,避免过度使用导致代码复杂度上升和性能下降。掌握装饰设计模式,能够让我们在面对功能扩展需求时,编写出更加简洁、灵活和可维护的代码。
1809

被折叠的 条评论
为什么被折叠?



