设计模式- 装饰器模式(Decorator Pattern)结构|原理|优缺点|场景|示例
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给对象添加新的职责(功能)或改变其原有行为。装饰器模式通过创建一个装饰器类,该类包装(持有)原始对象,并在保持原始对象接口不变的前提下,通过代理或继承的方式添加新的功能。装饰器模式可以提供比继承更灵活的扩展方式,因为它可以在不修改原有类的情况下,为对象添加新功能,并且可以叠加多个装饰器以实现多重增强
创建型(5种) 工厂方法 抽象工厂模式 单例模式 建造者模式 原型模式
结构型(7种) 适配器模式 装饰器模式 代理模式 外观模式 桥接模式 组合模式 享元模式
行为型(11种) 策略模式 模板方法模式 观察者模式 迭代器模式 责任链模式 命令模式 备忘录模式 状态模式 访问者模式 中介者模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给对象添加新的职责(功能)或改变其原有行为。装饰器模式通过创建一个装饰器类,该类包装(持有)原始对象,并在保持原始对象接口不变的前提下,通过代理或继承的方式添加新的功能。装饰器模式可以提供比继承更灵活的扩展方式,因为它可以在不修改原有类的情况下,为对象添加新功能,并且可以叠加多个装饰器以实现多重增强。
模式结构
装饰器模式通常包含以下角色:
组件接口(Component):定义了基础功能的接口,所有具体组件和装饰器都要实现这个接口,以便于保持接口的一致性。
具体组件(Concrete Component):实现了组件接口,是需要被装饰的基础对象。
装饰器(Decorator):实现了组件接口,并持有一个组件接口类型的引用,用于存储被装饰的对象。装饰器可以在实现组件接口方法的基础上,添加新的职责或修改原有行为。
具体装饰器(Concrete Decorators):继承自装饰器类,为具体组件添加新的职责或修改其行为。具体装饰器可以在构造函数中接收被装饰的对象,并在实现组件接口方法时调用被装饰对象的对应方法。
工作原理
- 客户端:创建具体组件对象,并根据需要通过装饰器对其进行装饰。客户端始终面向组件接口编程,无需关心对象是否被装饰以及如何装饰。
- 装饰器:持有一个组件接口类型的引用,用于存储被装饰的对象。装饰器在实现组件接口方法时,可以调用被装饰对象的对应方法,并在此基础上添加新功能或修改原有行为。
- 具体装饰器:在构造函数中接收被装饰的对象,并在实现组件接口方法时调用被装饰对象的对应方法,同时添加新的职责或修改原有行为。
优缺点
优点
- 开放封闭原则:装饰器模式遵循“开闭原则”,允许在不修改原有类的情况下,动态地为对象添加新功能,增强了系统的可扩展性。
- 灵活性:可以使用多个装饰器对同一个对象进行多次装饰,从而实现不同组合的增强功能。
- 透明性:装饰后的对象与未装饰的对象在对外接口上保持一致,客户端无需关心对象是否被装饰以及如何装饰,只需面向组件接口编程。
缺点
- 过度使用可能导致类层次过深:如果过度使用装饰器模式,可能会导致类的层次过深,增加系统的复杂性。
- 不易于理解:对于不熟悉装饰器模式的开发人员来说,可能需要花费更多时间理解装饰器的实现逻辑。
适用场景
- 需要为对象动态添加新功能,且新功能与原有功能可独立变化:装饰器模式可以在运行时为对象添加新功能,新功能与原有功能通过装饰器类进行封装,二者可以独立变化。
- 需要保持类的开放封闭原则,避免修改已有代码:装饰器模式可以在不修改原有类的情况下,通过创建装饰器类为对象添加新功能,符合开放封闭原则。
- 需要为对象提供多种可选的附加功能,且这些功能可以自由组合:通过使用多个装饰器对同一对象进行装饰,可以实现不同组合的增强功能。
代码示例(以Java为例)
// 组件接口
interface Coffee {
double getCost();
String getDescription();
}
// 具体组件
class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 10.0;
}
@Override
public String getDescription() {
return "Simple coffee";
}
}
// 装饰器
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
// 具体装饰器
class MilkCoffee extends CoffeeDecorator {
public MilkCoffee(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 2.0;
}
@Override
public String getDescription() {
return super.getDescription() + ", with milk";
}
}
class VanillaCoffee extends CoffeeDecorator {
public VanillaCoffee(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 3.0;
}
@Override
public String getDescription() {
return super.getDescription() + ", with vanilla";
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Coffee simpleCoffee = new SimpleCoffee();
System.out.println(simpleCoffee.getDescription() + " costs " + simpleCoffee.getCost());
// 输出:Simple coffee costs 10.0
Coffee milkCoffee = new MilkCoffee(simpleCoffee);
System.out.println(milkCoffee.getDescription() + " costs " + milkCoffee.getCost());
// 输出:Simple coffee, with milk costs 12.0
Coffee vanillaMilkCoffee = new VanillaCoffee(milkCoffee);
System.out.println(vanillaMilkCoffee.getDescription() + " costs " + vanillaMilkCoffee.getCost());
// 输出:Simple coffee, with milk, with vanilla costs 15.0
}
}
在这个Java示例中:
Coffee
接口作为组件接口,定义了获取咖啡价格和描述的方法。SimpleCoffee
类是需要被装饰的具体组件,实现了基础的咖啡功能。CoffeeDecorator
类作为装饰器,实现了Coffee
接口,并持有一个Coffee
类型的引用,用于存储被装饰的对象。装饰器类实现了getCost()
和getDescription()
方法,但并未添加新功能,主要是为了方便具体装饰器类的继承。MilkCoffee
和VanillaCoffee
类是具体装饰器,分别继承自CoffeeDecorator
,并在构造函数中接收被装饰的对象。它们在实现getCost()
和getDescription()
方法时,调用了被装饰对象的对应方法,并在此基础上添加了新功能(牛奶和香草)的费用和描述。- 客户端代码创建
SimpleCoffee
对象,并根据需要通过装饰器对其进行装饰,最终得到添加了牛奶和香草的咖啡。客户端始终面向Coffee
接口编程,无需关心对象是否被装饰以及如何装饰。
代码示例(以Python为例)
# 组件接口
class Coffee:
def get_cost(self):
raise NotImplementedError("Subclasses must implement this method")
def get_description(self):
raise NotImplementedError("Subclasses must implement this method")
# 具体组件
class SimpleCoffee(Coffee):
def __init__(self):
self.cost = 10
self.description = "Simple coffee"
def get_cost(self):
return self.cost
def get_description(self):
return self.description
# 装饰器
class CoffeeDecorator(Coffee):
def __init__(self, decorated_coffee: Coffee):
self.decorated_coffee = decorated_coffee
def get_cost(self):
return self.decorated_coffee.get_cost()
def get_description(self):
return self.decorated_coffee.get_description()
# 具体装饰器
class MilkCoffee(CoffeeDecorator):
def __init__(self, decorated_coffee: Coffee):
super().__init__(decorated_coffee)
self.cost = decorated_coffee.get_cost() + 2
self.description = decorated_coffee.get_description() + ", with milk"
class VanillaCoffee(CoffeeDecorator):
def __init__(self, decorated_coffee: Coffee):
super().__init__(decorated_coffee)
self.cost = decorated_coffee.get_cost() + 3
self.description = decorated_coffee.get_description() + ", with vanilla"
# 客户端代码
def main():
simple_coffee = SimpleCoffee()
print(simple_coffee.get_description(), "costs", simple_coffee.get_cost()) # 输出:Simple coffee costs 10
milk_coffee = MilkCoffee(simple_coffee)
print(milk_coffee.get_description(), "costs", milk_coffee.get_cost()) # 输出:Simple coffee, with milk costs 12
vanilla_milk_coffee = VanillaCoffee(milk_coffee)
print(vanilla_milk_coffee.get_description(), "costs", vanilla_milk_coffee.get_cost()) # 输出:Simple coffee, with milk, with vanilla costs 15
if __name__ == "__main__":
main()
在这个Python示例中:
Coffee
类作为组件接口,定义了获取咖啡价格和描述的接口。SimpleCoffee
类是需要被装饰的具体组件,实现了基础的咖啡功能。CoffeeDecorator
类作为装饰器,实现了组件接口,并持有一个Coffee
类型的引用,用于存储被装饰的对象。装饰器类实现了get_cost()
和get_description()
方法,但并未添加新功能,主要是为了方便具体装饰器类的继承。MilkCoffee
和VanillaCoffee
类是具体装饰器,分别继承自CoffeeDecorator
,并在构造函数中接收被装饰的对象。它们在实现get_cost()
和get_description()
方法时,调用了被装饰对象的对应方法,并在此基础上添加了新功能(牛奶和香草)的费用和描述。- 客户端代码创建
SimpleCoffee
对象,并根据需要通过装饰器对其进行装饰,最终得到添加了牛奶和香草的咖啡。客户端始终面向Coffee
接口编程,无需关心对象是否被装饰以及如何装饰。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)