我觉得Decorator是比较透着灵气的设计模式,至于这句话怎么理解我也不好解释,呵呵,切入正题吧。
先看类图了:
注意到图中的一个环了吗,就是Decorator继承自Component,然后又引用Component的那个环路,就是它可以让我们不断动态的添加新的功能到Component 定义的一个对象。这也正是Decorator要解决的问题,为一个对象动态连接附加的职责。
下面看代码:
假设一个SalesTicket类有一个打印功能,现在新的需求提出要增加打印一个台头和一个页脚,当然你可以用简单继承来实现,但是如果出现新的台头和页脚呢,如果需求还要求不同台头和页脚的组合,如果只是使用继承,你会发现多了一堆代码重复的“超生类”。
我们下面还是使用decorator模式来实现。
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
IComponent test = new Header1(new Header2(new Footer1 ((new SalesTicket())))); //注意这里是组合,可以放在配置文件中,核在最后
test.prtTicket();
Console.ReadLine();
}
}
public interface IComponent
{
void prtTicket();
}
public class SalesTicket : IComponent // 这个是包装的核
{
void IComponent.prtTicket()
{
Console.WriteLine("Print Ticket Body");
}
}
public class TicketDecorator : IComponent //与核为同一个接口
{
public IComponent _myC;
public TicketDecorator(IComponent myC)
{
_myC = myC;
}
public virtual void prtTicket()
{
if (_myC != null) _myC.prtTicket();
}
}
public class Header1 : TicketDecorator
{
public Header1(IComponent myC):base(myC){} //注意这里的写法。因为在派生类中,如果不使用 base 关键字来显式调用基类构造函数,则将隐式调用默认构造函数(如果有的话)。
public override void prtTicket()
{
Console.WriteLine("Header1 Is Here");
_myC.prtTicket(); //包装的是同名函数
}
}
public class Header2 : TicketDecorator
{
public Header2(IComponent myC): base(myC) {}
public override void prtTicket()
{
Console.WriteLine("Header2 Is Here");
_myC.prtTicket();
}
}
public class Footer1 : TicketDecorator
{
public Footer1(IComponent myC): base(myC) {}
public override void prtTicket()
{
_myC.prtTicket();
Console.WriteLine("Footer1 Is Here");
}
}
}
}
仔细看客户端代码,通过任意的组合我们实现不同的功能,这种组合可以无限的,因为大家接口都一样,可以套在一起,各司其职。
注意最后是以包装的核结尾的。
相比单独继承的好处,上面已经说了,灵活的组合显然好于僵硬的继承。
(还有什么要点明天总结,困了)