装饰,字面意思是对生活用品或生活环境进行艺术加工的手法。它必须与所装饰的客体有机地结合,成为统一、和谐的整体,以便丰富艺术形象,扩大艺术表现力,加强审美效果,并提高其功能、经济价值和社会效益。我们编程世界中的装饰又有着怎样与众不同的解释呢?原来装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
我们来看一个具体的例子,经过一个上午的消耗,加上昨天晚上熬夜看《出彩中国人》,早上赖床,没有去吃饭,那上午叫一个饿啊,于是发誓,再也不熬夜了,咳咳,到了晚上,拿起手机什么又都忘了,于是,想着赶紧下课,去吃饭,最好来一碗面条,来点儿辣椒再来点儿醋,那味道,呼呼流口水了,来到卖面条的地方,这个点儿来的人可真多啊,咱是好孩子,得排队是不是,顺便看一下标签上有哪些面条,哇塞面条的种类可真多啊,比如有雪菜肉丝面条,西红柿鸡蛋面条,小鸡蘑菇面条,听,我前面的小姑娘要了一碗西红柿鸡蛋面条,紧接着一个男孩要了一碗小鸡蘑菇面条,每个同学的选择是不同的,也就是需求是各种各样的,那么这种情况在我们的编程世界中如何实现呢,这个时候,排队的我想到了继承,如下图所示:
这个时候如果我想要加醋和加辣椒的面条?我要怎么办,可以通过继承来实现扩展,但是这样的设计有点儿笨笨的,于是一种新设计模式--装饰模式就这样横空出世了,我们来看看装饰模式的结构图:
装饰者模式呢,其实可以看做是一种在已有功能上动态添加新的功能的一种方式,在不用装饰者模式的前提下,如果要在已有的功能上添加新功能,一般都是可以使用继承的,但是,继承的缺点呢,在上面的例子中也暴露的很明显,同时,使用继承的话,添加功能不是动态的,因为子类完全继承了父类,而使用装饰者模式的话,您可以在客户端按照需求一个一个的包装对象,通过包装对象来添加新功能,这样便实现了动态添加新功能,比如,我可以对 Component 通过 ConcreteDecoratorA 来包装一个 State 状态,或者是通过 ConcreteDecoratorB 来包装一个新的行为(功能)Behavior ,以我们的面条例子为例,看看我们的程序是怎么实现的呢?
先来看一下Eat类:
<span style="font-size:18px;">using System; namespace Decorator { public abstract class Noodle { /// <summary> /// 在抽象类中只定义了一个抽象接口 /// 然后可以通过这个抽象接口来给对象动态的添加功能 /// </summary> public abstract void ShowNoodle(); } } </span>然后就是一个Noodle类:
<span style="font-size:18px;">using System; namespace Decorator { public class Noodle : Eat { private string name; public Noodle(string name) { this.name = name; } /// <summary> /// 给当前的对象添加一些功能 /// 比如这里就是指定了面条的名称 /// 这里添加的功能是静态添加的 /// </summary> public override void ShowEat() { Console.WriteLine("面条名称为:{0} ", this.name); } } }</span>下面再来看 DecoratorEat 类:
<span style="font-size:18px;">namespace Decorator { public class DecoratorEat: Noodle { /// <summary> /// 在装饰类中必须要保存一个对于对象的引用 /// </summary> protected Eat eat; public DecoratorEat(Eat eat) { this.eat = eat; } public override void ShowEat() { if (Eat != null) { Eat.ShowEat(); } } } } </span>还有就是装饰类 Tomato:
<span style="font-size:18px;">using System; namespace Decorator { public class Tomato : DecoratorEat { /// </summary> /// <param name="Eat"></param> public Tomato(Eat eat) : base(eat) { } public override void Showeat() { //首先必须要调用父类的 ShowEat base.ShowEat(); //然后下面就可以添加新功能了 Console.WriteLine("加西红柿 "); } } } </span>装饰类鸡蛋和豆皮的代码跟上述装饰类西红柿雷同,再此不一一赘述,接下来,我们一起看看客户端的代码:
<span style="font-size:18px;">using System; using Decorator; namespace DecoratorTest { class Program { static void Main(string[] args) { Noodle noodle = new Noodle("面条"); //给面条加西红柿,也就是使用西红柿来装饰面条 Tomato tomato = new Tomato(noodle); //给加了西红柿的面条加鸡蛋,也就是使用加鸡蛋来装饰面条 Egg egg = new Egg(Tomato); //显示出当前面条的状态 Egg.ShowNoodle(); Console.WriteLine(); noodle = new Noodle("面条"); tomato = new Tomato(noodle); //给加了西红柿的面条加豆皮 Doupi doupi = new Doupi(Tomato); //给加了西红柿和鸡蛋的面条加个豆皮 Doupi doupi = new Doupi(doupi); doupi.ShowEat(); Console.ReadLine(); } } }</span>通过装饰模式我们的小菜有着百搭的风格,而我也.......嘻嘻,再回到我们的装饰模式中来,装饰模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。设计之旅,未完待续......