装饰者,decorator,定义是:动态将责任附加到对象上,若要扩展功能,装饰比继承提供了更具弹性的的替代方案。
定义很简单,而且每个人大概都有个模糊的概念和理解,但装饰者到底是为解决什么问题的?什么情况下可以应用这种模式?它到底是怎么样的?
从定义上,可以找到几个关键词:动态,责任,附加,对象,扩展,弹性。
动态,表示它不是死的,固定的。
责任,这里指对象的功能,类的作用,例如,行为。
附加,表示它是一种次要的事物,要依附主要事物。
扩展,表示它不是封闭的。
弹性,物理上表示物体受到外力发生形变,除去外力后恢复原来状态。它指的是一个量因受到另一个量的变化而变化。如果一个量变化大,那它的量变化也大,反之则小。弹性大,那么它的可伸缩的空间就大。
从字面上联系现实世界来看,装饰可以说是人的着装打扮。人有很多种,也有很多个,服装和化妆品也有很多种,很多个。因为文化、环境、个人等因素的存在,所以着装打扮也不尽相同。一个人可以穿休闲服,可以带上一个墨镜,如果下雨的话,还可以带上一个雨伞,或穿上一件雨衣。这些都可以形容为是一种装饰,它灵活。一个人可以穿多种多样的衣服,可以画很多不同的妆(只要想),那么说明装饰是可扩展的,它扩展的是一个人的外在表现。当然也可以添加一块手表,一个墨镜,那么它可以看时间,可以遮挡强光(还可以改变外貌,使人更酷),那么现在就扩展了人的行为:可以看时间,可以在强光下长时间看。
如果是冬天,天气寒冷,那么人可以穿保暖性强的服装,如棉服;如果是夏天,天气炎热,那么可以穿薄点的,通气性好的服装,如Tshirt,或不穿。
环顾周围,可以发现很多对象,而且很容易的说出它们的属性,例如:杯子,房子,鼠标,笔,手机等。但把现实世界的对象抽象为软件中的类型和对象时,会发现,并不容易。面向对象的设计与现实世界的对象是映射的。那么现实中解决问题的方法和面向对象的设计中的各种原则,例如设计模式也是对应的。如果在面向对象的软件设计和开发过程中,把两者联系起来,会发现,一切都是那么的合情合理。
现在描述一个场景:一个有老师,工程师,小学生的环境下(话剧的化妆间),每个人的穿着不一样,有穿学生装的,有带电脑的,有带领带的等。他们都要通过化妆师来化妆打扮以适合各自的角色,假设他们都穿着内衣来进行化妆。那么学生要穿:学生装,红领巾,短裤;老师要穿:长衣衫,长裙,平底鞋,眼镜;工程师:tshirt,裤子,皮鞋,带电脑,带领带。
在化妆间,发现,这些学生装,红领由,电脑,眼镜等都是独自存在的,而不是来就带有的(假设)。那到哪个角色,就可以为这个角色以角色的特征来添加相应的衣服和配套用品。
穿是人的行为,老师,小学生,工程师都能执行这个行为。
装饰者的装饰对应的功能是主要和次要类中所共有的,在这个例子中,他们有共同的行为:穿。然后被装饰对象和装饰对象拥有相同的抽象,也就是具有同一个父类,那么才能可能利用多态来实现装饰(包装)。装饰者模式有一个要素:
被装饰者(主要的,人,小学生,工程师,老师)和装饰者(各种服装,配套用品,皮鞋,电脑,眼镜等)共同的接口:这里就是人,精确的说是人的行为:穿。而为这个穿行为,主要者:各种职业有基本的穿着行为。然后通过装饰各种次要者,来实现各种角色服饰的目的。其中服装和配套用品可以有很多种,它是灵活的。可以要哪种服装加哪种。
(一)基类:其中有主要和次要的共同接口行为:Dress
{
public void Display()
{
Dress();
}
public abstract void Dress();
}
(二)装饰者
从基类继承,且类中有要装饰的对象:
{
protected Person _person;
public Decorator(Person person)
{
_person = person;
}
public override void Dress()
{
throw new NotImplementedException();
}
}
(三)装饰者实现
实现装饰者,做为各种服装,配套类。如长裙类。
{
public Longuette(Person person):base(person)
{ }
public override void Dress()
{
_person.Dress();
Console.WriteLine("longuette");
}
}
(四)被装饰实现
主要的实现,例如学生
{
public override void Dress()
{
Console.WriteLine("student's");
}
}
使用:
TShirt t = new TShirt(person);
t.Display();
装饰者会把被装饰者包装起来。这个包装的过程就是在装饰者中保留了对父类的一个的引用,同时装饰者继承了父类,当对一个从父类派生的被装饰者进行装饰时,可以调用这个引用的功能,同时对同一功能来说,还要加上装饰者本身的这个行为,做为一种装饰存在。实现了功能的附加。
装饰者模式的前提是装饰者和被装饰者具有相同的父类型。
它的定义是这样的:定义一个共同的父类,然后定义一个装饰抽象类,从这个父类派生。这个装饰抽象类中,保留一个父类的实例引用。定义一个被装饰者,也从这个父类派生。
当使用时,把被装饰者实例传递到装饰者,从另一形式上说,装饰者包起了被装饰者的实例,那么在执行行为时,可以在执行派生的行为外,还能通过保留的实例引用使用被包装对象的这个行为。这样就完成了职责的附加,扩展了功能。
现在考虑一个场景:设计一块电子表,它有最基本的计时功能。然后,为了用于各种不同的用途,例如:潜水用,森林里用,黑夜用等,可以为它添加不同的接口行为。可以添加指南针功能,可以增加防水功能,可以增加夜视灯功能。它的可附加功能有很多,且各个附加功能可能多个使用。在这种情况下,可以设计为装饰模式。
首先,要抽象出主要和次要的共同父类,这里把电子表做为这个父类,且这个父类具有最基本的计时能力。
{
public virtual void Capability()
{
Console.WriteLine("计时");
}
}
其次,设计装饰抽象,把父类引用保留在这个类中,同时在构造时把被装饰者传给这个引用,以调用这个被装饰者的能力。
{
protected Watch _watch;
public WatchDecorator(Watch watch)
{
_watch = watch;
}
public override void Capability()
{
_watch.Capability();
}
}
然后设计多个装饰实现者,实现要附加的功能:防水,夜视,指南针。
{
public Waterproof(Watch watch)
: base(watch)
{ }
public override void Capability()
{
_watch.Capability();
Console.WriteLine("防水");
}
}
public class NightSight : WatchDecorator
{
public NightSight(Watch watch)
: base(watch)
{ }
public override void Capability()
{
_watch.Capability();
Console.WriteLine("夜视");
}
}
public class Compass : WatchDecorator
{
public Compass(Watch watch)
: base(watch)
{ }
public override void Capability()
{
_watch.Capability();
Console.WriteLine("指南针");
}
}
watch.Capability();
Console.WriteLine("****************************");
//添加夜视和指南功能
watch = new NightSight(watch);
watch = new Compass(watch);
watch.Capability();
装饰,或其他模式,或面向对象的设计的原则等等,如果与真实世界联系起来,会发现它们一切都在情理之中,且有的或有时比真实的世界更理想。好比艺术,来源于生活,但高于生活。同样,这些优秀的经验来自于真实的世界,且有时还高于这个世界。
