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