思考并回答以下问题:
现在要求写一个可以给人搭配不同的服饰的系统。
第一版
结构图
Person类
1 | class Person |
客户端代码
1 | class Program |
结果显示
1 | 第一种装扮: |
如果需要增加‘超人’的装扮,就要改‘Person’类,违背了开放-封闭原则。应该把这些服饰都写成子类。
第二版
代码结构图
Person类
1
2
3
4
5
6
7
8
9
10
11
12
13
14 class Person
{
private string name;
public Person(string name)
{
this.name = name;
}
public void Show()
{
Console.WriteLine("装扮的{0}", name);
}
}服饰抽象类
1 | // 服饰 |
各种服饰子类
1 | // 大T恤 |
客户端代码
1 | class Program |
结果显示同前例,略。
如果要加超人装扮,只要增加子类就可以了。
用了继承,用了抽象类就算是用好了面向对象了吗?现在的代码的确做到了‘服饰’类与‘人’类的分离,但其他问题还存在。
1 | dtx.Show(); |
这样写就好比:你光着身子,当着大家的面,先穿T恤,再穿裤子,再穿鞋,仿佛在跳穿衣舞。在内部组装完毕,然后再显示出来,这是建造者模式。建造者模式要求建造的过程必须是稳定的,而现在这个例子,建造过程是不稳定的,比如完全可以内穿西装,外套T恤,再加披风,打上领带,皮鞋外再穿上破球鞋;当然也完全可以只穿条裤衩就算完成。换句话就是说,通过服饰组合出一个有个性的人完全可以有无数种方案,并非是固定的。
需要把所需的功能按正确的顺序串联起来进行控制,这可以用一个非常有意思的设计模式来实现。
装饰模式
1 | 装饰模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。 |
装饰模式(Decorator)结构图
Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。
基本的代码实现。
Component类
1 | abstract class Component |
ConcreteComponent类
1 | class ConcreteComponent : Component |
Decorator类
1 | abstract class Decorator : Component |
具体装饰者类
1 | class ConcreteDecoratorA : Decorator |
客户端代码
1 | class Program |
装饰模式是利用SetComponent来对对象进行包装的。这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。用刚才的例子来说就是,我们完全可以先穿外裤,再穿内裤,而不一定要先内后外。
‘人’类是Component还是ConcreteComponent呢?
学习模式要善于变通,如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
在这里就没有必要有Component类了,直接让服饰类Decorator继承人类ConcreteComponent就可。
第三版
代码结构图
Person类(ConcreteComponent)
1 | class Person |
服饰类(Decorator)
1 | class Finery : Person |
具体服饰类(ConcreteDecorator)
1 | class TShirts : Finery |
客户端代码
1 | class Program |
结果显示
1 | 第一种装扮: |
换一种装饰方式
1 | Console.WriteLine("\n第三种装扮:"); |
结果显示
1 | 第三种装扮: |
装饰模式总结
装饰模式是为已有功能动态地添加更多功能的一种方式。但到底什么时候用它呢?
起初的设计中,当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为,比如用西装或嘻哈服来装饰小菜,但这种做法的问题在于,它们在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,就像你起初的那个‘人’类,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。所以就出现了上面那个例子的情况,我可以通过装饰,让你全副武装到牙齿,也可以让你只挂一丝到内裤。
装饰模式的优点总结下来就是,把类中的装饰功能从类中搬移去除,这样可以简化原有的类。
这样做更大的好处就是有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。
装饰模式的装饰顺序很重要哦,比如加密数据和过滤词汇都可以是数据持久化前的装饰功能,但若先加密了数据再用过滤功能就会出问题了,最理想的情况,是保证装饰类之间彼此独立,这样它们就可以以任意的顺序进行组合了。