Posts Decorator模式-用装修的概念自由组合功能
Post
Cancel

Decorator模式-用装修的概念自由组合功能

Decorator模式从名字上来看就是有一点装修房子的感觉。比如在毛坯房的基础上,可以层层装修、丰富完善一个房子。它的使用场景针对一个已经存在的功能,想要在此基础上添加一些新的功能。但是这些新添加的功能不一定是确定的,可以根据需要添加一批功能,也可能想添加另外一组功能。

举例: 有一些紧急的通知,平时都是通过发送邮件的方式通知人员。后来有新的需求,想要同时发送邮件和短信。再然后,想同时发送邮件、短信和拨打电话给人员。又可能在邮件系统当机的情况下,只发送短信和拨打电话。这些发送通知的方式,可以是一个自由组合。

如果把发送邮件这个基本功能当成一个毛坯房,那发送短信和拨打电话就像是给毛坯房做一个装修,要么只发送短信,要么只拨打电话,要么同时都有。

构造毛坯房

把上面的逻辑用Decorator模式设计一下。先构造一个基本的毛坯房,就是一个基本功能。

1
2
3
4
abstract class AbstractAlert
{
  public abstract void Send();
}

.

1
2
3
4
5
6
7
class EmailAlert : AbstractAlert
{
  public override void Send()
  {
    Console.WriteLine("Send alert via Email");
  }
}

抽象方法 Send() 定义了规格。

设计一个装饰器的抽象

装饰器的目的是为了装修毛坯房本身,用对象委托的方式,将要被装修的毛坯房(或半成品房)包裹起来,在实现Send()的抽象方法时,调用这个被委托对象自己的 Send()功能。

因为这个是装饰器是一个抽象的类,Send中不会定义具体的功能,但会调用被委托对象的Send的功能。至于后面的每一个装饰,会继承装饰器的Send,然后会定义自己的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class AbstractAlertDecorator : AbstractAlert
{
  protected AbstractAlert alert;
  public AbstractAlertDecorator(AbstractAlert alert)
  {
    this.alert = alert;
  }

  public override void Send()
  {
    if (alert != null)
    {
      alert.Send();
    }
  }
}

定义每一个装饰

比如现在发送短信和拨打电话,实现抽象装饰器。同时在Send实现中,必须做两件事情:

  • 调用父类也就是抽象装饰器的Send,目的是间接地调用被委托对象的Send

  • 做自己的具体的逻辑:发消息或打电话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SmsAlert : AbstractAlertDecorator
{
  public SmsAlert(AbstractAlert alert) : base(alert)
  {
  }

  public override void Send()
  {
    base.Send();

    // the logic to send SMS
    Console.WriteLine("Send alert via SMS");
  }
}

.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PhoneAlert : AbstractAlertDecorator
{
  public PhoneAlert(AbstractAlert alert) : base(alert)
  {
  }

  public override void Send()
  {
    base.Send();

    // the logic to send Phone
    Console.WriteLine("Send alert via Phone");
  }
}

开始装修

比如同时发送邮件、短信和电话,可以这么调用:

1
2
3
4
AbstractAlert email = new EmailAlert();
AbstractAlertDecorator sms = new SmsAlert(email);
AbstractAlertDecorator phone = new PhoneAlert(sms);
phone.Send();

如果只是短信和电话,可以这么用:

1
2
3
AbstractAlertDecorator sms = new SmsAlert(null);
AbstractAlertDecorator phone = new PhoneAlert(sms);
phone.Send();

无论哪一组功能的组合,委托都是通过抽象装饰器提供来实现,因为原始的毛坯房也就是 AbstractAlert 没有提供委托功能。

其他相似模式的比较

这种自由组合功能的方式,也许可以通过 AbstractAlert 的多个继承来实现,但是远没有这种装饰模式来得简洁。因为用继承的方式,要连续调用多个实例的Send方法,较为累赘。而装饰器模式,只调用最外面一层装修物的Send就可以了。

如果把Email当成毛坯房,SMS当成半成品房,那么Phone可能就是成品房,这三者都有一致性。后者把前者包裹起来,有点像Composite模式。但是Composite关注的是数据的容器特征。而Decorator关注的功能的组合叠加。

This post is licensed under CC BY 4.0 by the author.

Composite模式-用统一的眼光看事物

Facade模式-隐藏细节