设计模式之装饰模式
装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
上图为装饰模式的结构图,Component定义一个对象接口可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责(ConcreteComponent主要就是作为一个具体的对象存在)。Decorator,装饰抽象类,继承自Component,从外类扩展Component类的功能,相对于Component来说无需知道Decorator存在。ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。
P.S:如果只有一个ConcreteComponent而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDector类(即只有一个具体的装饰类),那么就没必要单独建立一个Decorator类,可以把Decorator和ConcreteDecorator的职责合并。
从上图中可以看到装饰模式的四要素:
1.装饰的接口即Component;
2.装饰的具体对象ConcreteComponent,用于实现添加职责的功能;
3.抽象的装饰类,同时继承自Component;
4.具体的装饰类,继承自抽象的装饰类。
Component:
abstract class Component { public abstract void Operation(); }
ConcreteComponent:
class ConcreteComponent:Component { public override void Operation() { Console.WriteLine("具体具体操作"); } }
Decorator抽象装饰类:
abstract class Decorator : Component { protected Component component; public Decorator() { } /// <summary> /// 设置Component /// </summary> /// <param name="component"></param> public void SetComponent(Component component) { this.component = component; } /// <summary> /// 重写Operation方法,实际执行的是Component的Operation /// </summary> public override void Operation() { if (component != null) { component.Operation(); } } }
具体装饰类:
class ConcreteDecoratorA:Decorator { public override void Operation() { //先执行基类的Operation base.Operation(); Console.WriteLine("具体装饰类A的操作"); } } class ConcreteDecoratorB : Decorator { public override void Operation() { //先执行基类的Operation base.Operation(); Console.WriteLine("具体装饰类B的操作"); } } class ConcreteDecoratorC : Decorator { public override void Operation() { //先执行基类的Operation base.Operation(); Console.WriteLine("具体装饰类C的操作"); } }
调用方法:
ConcreteComponent component = new ConcreteComponent(); ConcreteDecoratorA c1 = new ConcreteDecoratorA(); ConcreteDecoratorB c2 = new ConcreteDecoratorB(); c1.SetComponent(component); c2.SetComponent(c1); c2.Operation();
调用效果如下:
装饰的过程:先定义了具体的Component类ConcreteComponent,然后使用ConcreteDecoratorA 的实例化对象c1来包装component,然后用ConcreteDecoratorB的对象c1来包装c1,最终执行c2的Operation方法。其实装饰模式最主要的是使用SetComponent来对对象进行包装的。这样每个装饰对象的实例就和如何使用这个对象分离开了,每个装饰对象只关心如何被添加到对象链当中。
整个过程是层层传递的,最终会先执行被传递的那个Component对象(也可以说是具体的装饰对象),整个的过程主要通过SetComponent进行完成,以及在具体对象的Operation中执行base的操作(这样才可以执行到传递的Component的行为,其实就是一个继承的作用)。
下面举例一个穿衣服的装饰模式例子:
场景:衣服有很多种,T恤,裤子,西装,运动鞋,皮鞋,领带等,至于怎么组合无需关心,交给客户决定。
分析:衣服要有人来穿,所以人是一个具体的Component,上述的各种衣服最终还是一个衣服,所以一个衣服的积累,作为抽象的装饰存在,另外各种衣服就是一个个具体的装饰对象。
人(Person具体的Component):
class Person { public Person() { } //当前人 private string name; public Person(string name) { this.name = name; } /// <summary> /// 展示衣服 /// </summary> public virtual void Show() { Console.WriteLine(string.Format("装扮的{0}", name)); } }
衣服(服饰的基类):
/// <summary> /// 衣服类 /// </summary> class Finery:Person { protected Person component; /// <summary> /// 设置打扮的衣服 /// </summary> /// <param name="component"></param> public void Decorate(Person component) { this.component = component; } public override void Show() { if (component!=null) { component.Show(); } } }
其他具体的服饰类:
/// <summary> /// T恤 /// </summary> class TShirt:Finery { public override void Show() { base.Show(); Console.WriteLine("T恤"); } } /// <summary> /// 裤子 /// </summary> class Trouser:Finery { public override void Show() { base.Show(); Console.WriteLine("裤子"); } } /// <summary> /// 运动鞋 /// </summary> class SportShoes:Finery { public override void Show() { base.Show(); Console.WriteLine("运动鞋"); } } /// <summary> /// 西装 /// </summary> class Suit:Finery { public override void Show() { base.Show(); Console.WriteLine("西装"); } } /// <summary> /// 皮鞋 /// </summary> class EatherShoes:Finery { public override void Show() { base.Show(); Console.WriteLine("皮鞋"); } } /// <summary> /// 领带 /// </summary> class Tie:Finery { public override void Show() { base.Show(); Console.WriteLine("领带"); } }
调用:
Console.WriteLine("第一种打扮:"); Person person = new Person("Blue"); TShirt shirt = new TShirt(); Trouser trouser = new Trouser(); SportShoes sportShoes = new SportShoes(); //开始装饰了 shirt.Decorate(person); trouser.Decorate(shirt); sportShoes.Decorate(trouser); //开始展示衣服了 sportShoes.Show(); Console.WriteLine(); Console.WriteLine("第二种打扮:"); Suit suit = new Suit(); EatherShoes eatherShoes = new EatherShoes(); Tie tie = new Tie(); //再次装饰了 suit.Decorate(person); eatherShoes.Decorate(suit); tie.Decorate(eatherShoes); //开始展示衣服了 tie.Show();
代码中一共展示了两种不同的装扮,除了实例化的衣服不同之外,其余过程均相同。实例化一个Person类,即实例化一个具体的Component,然后开始实例化各种各样的衣服,然后通过Decorate方法进行装饰,最后调用Show进行衣服的展示,效果如下:
现在如果想增加一种衣服怎么做呢,很简单,增加一个衣服类,继承自Finery类,重写Show方法即可。如果要换装扮,也很简单,只需定义需要的衣服类型,并且进行装饰即可完成,代码是不是非常的低耦合呢。
后记总结:
装饰模式是为已有功能动态地添加更多功能的一种方式。当系统需要新功能时,装饰模式提供了一个很好的方案,它把每个要装饰的功能放在了单独的类中,并让这个类包装它所需要的对象,因此当需要执行特殊行为时,客户代码就可以在运行时有选择地使用装饰功能包装对象。
优点:把类的核心职责和装饰功能区分开了,而且去除相关类中重复的装饰逻辑。
好了,此次模式到此结束,欢迎大家多提意见和建议。