修饰器模式(结构型模式)
模式概述
作为一种结构型设计模式,修饰器模式通过将对象放入另一个封装对象中来为原对象绑定新的行为,它有点像俄罗斯套娃。

问题
有一个通知器Notifier
类,它有几个成员变量、一个构造函数和一个send()发送方法。
send()
方法可以接收来自客户端的消息参数,并将该消息发送给指定的邮箱列表。
客户端仅会创建和配置通知器对象一次,然后在有重要事件发生时对其进行调用。
但现在除了要发送邮件通知,还需要发送短信、微信及QQ消息。

如果扩展这个Notifier类,在新的子类中增加新的通知方法就可以了。
但是,如果现在想实现通知功能的组合,那子类的数量就会迅速增长。

方案
当需要修改或扩展某个对象的行为时,首先想到的就是用子类来继承它。
但是,这有几个问题。
继承是静态的,且无法在运行时更改已有对象的行为。
大部分编程语言不允许一个类同时继承多个类。
可以通过聚合或组合而不是继承来解决这个问题。

聚合或组合是许多设计模式背后的核心方法(包括装饰器模式)。
在Notifier类中,可以将邮件通知行为放在父类通知器中,而将所有其他通知方法放入修饰器中。

结构

实现
Java
/**
* 通知接口
*
*/
public interface Notifier {
void send(String message);
}
/**
* 具体通知
*
*/
public class MailNotifier implements Notifier {
private String name;
public MailNotifier(String name) {
this.name = name;
}
@Override
public void send(String message) {
System.out.println("发送邮件通知");
}
}
/**
* 修饰器基类
*
*/
public class BaseDecorator implements Notifier {
private Notifier wrappee;
BaseDecorator(Notifier notifier) {
this.wrappee = notifier;
}
@Override
public void send(String message) {
wrappee.send(message);
}
}
/**
* SMS修饰器
*
*/
public class SMSDecorator extends BaseDecorator {
public SMSDecorator(Notifier notifier) {
super(notifier);
}
@Override
public void send(String message) {
super.send(sms(message));
}
private String sms(String message) {
System.out.println("发送短信通知");
}
}
/**
* 微信修饰器
*
*/
public class WeixinDecorator extends BaseDecorator {
public WeixinDecorator(Notifier notifier) {
super(notifier);
}
@Override
public void send(String message) {
super.send(weixin(message));
}
private String weixin(String message) {
System.out.println("发送微信通知");
}
}
/**
* 客户端
*
*/
public class Client {
public static void main(String[] args) {
String message = "Hello World";
BaseDecorator decorator = new WeixinDecorator(
new SMSDecorator(
new MailNotifier()));
decorator.send(message);
}
}
Go
package main
import "fmt"
/**
* 披萨
*
*/
type IPizza interface {
getPrice() int
}
/**
* 披萨类别
*
*/
type VeggieMania struct {
}
func (p *VeggieMania) getPrice() int {
return 15
}
/**
* 浇番茄酱
*
*/
type TomatoTopping struct {
pizza IPizza
}
func (c *TomatoTopping) getPrice() int {
pizzaPrice := c.pizza.getPrice()
return pizzaPrice + 7
}
/**
* 浇芝士酱
*
*/
type CheeseTopping struct {
pizza IPizza
}
func (c *CheeseTopping) getPrice() int {
pizzaPrice := c.pizza.getPrice()
return pizzaPrice + 10
}
/**
* 客户端
*
*/
func main() {
pizza := &VeggieMania{}
pizzaWithCheese := &CheeseTopping{
pizza: pizza,
}
pizzaWithCheeseAndTomato := &TomatoTopping{
pizza: pizzaWithCheese,
}
fmt.Printf("浇了番茄酱和芝士酱的veggeMania披萨价格:%d\n", pizzaWithCheeseAndTomato.getPrice())
}
适用场景
如果希望在无需修改代码的情况下就能动态地为对象新增或撤销额外的行为,则可以使用修饰器模式。
如果通过继承方式来扩展对象行为的目的难以实现或者根本不可行,则可以使用修饰器模式。
优缺点
修饰器模式的优点。
抽象工厂模式隔离了具体产品的生成,实现了具体产品与客户端的解耦。
当引入新的事先定义的产品族时,无需修改代码,符合
开闭原则
。当多个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
修饰器模式的缺点。
- 对于多次修饰的对象,调试时寻找错误可能较为烦琐。
相关性
适配器模式
可以对已有对象的接口进行修改,而修饰器模式
则能在不改变对象接口的前提下强化对象功能。另外,修饰器模式
还支持递归组合,适配器模式
则无法实现。适配器模式
能为被封装对象提供不同的接口,代理模式
能为对象提供相同的接口,修饰器模式
则能为对象提供加强的接口。修饰器模式
可以更改对象的外表,而策略模式
则能够改变其本质。责任链模式
和修饰器模式
的类结构比较相似。两者都将需要执行的操作传递给一系列对象。但区别在于责任链模式
可以相互独立地执行所有操作,还可以随时停止传递请求。而修饰器模式
则无法中断请求的传递。组合模式
和修饰器模式
的结构图相似,因为两者都依赖递归和组合来组织无限数量的对象。可以通过
原型模式
来控制组合模式
和修饰器模式
的复杂结构。修饰器模式
与代理模式
的结构相似,都将部分工作委派给零一对象,但它们的目的不同。代理模式
通常自行管理服务对象的生命周期, 而修饰器模式
的生命周期总是由客户端来控制。
感谢支持
更多内容,请移步《超级个体》。