大道至简

无尽
XP agile SE
UT Mock T
  博客园 :: 首页 ::  :: 联系 :: 订阅 订阅 :: 管理

公告

DesignPattern2s——(3)decorator

Posted on 2011-08-30 11:20 梅桦 阅读(100) 评论(0) 编辑 收藏

装饰者,decorator,定义是:动态将责任附加到对象上,若要扩展功能,装饰比继承提供了更具弹性的的替代方案。

定义很简单,而且每个人大概都有个模糊的概念和理解,但装饰者到底是为解决什么问题的?什么情况下可以应用这种模式?它到底是怎么样的?

从定义上,可以找到几个关键词:动态,责任,附加,对象,扩展,弹性。

动态,表示它不是死的,固定的。

责任,这里指对象的功能,类的作用,例如,行为。

附加,表示它是一种次要的事物,要依附主要事物。

扩展,表示它不是封闭的。

弹性,物理上表示物体受到外力发生形变,除去外力后恢复原来状态。它指的是一个量因受到另一个量的变化而变化。如果一个量变化大,那它的量变化也大,反之则小。弹性大,那么它的可伸缩的空间就大。

从字面上联系现实世界来看,装饰可以说是人的着装打扮。人有很多种,也有很多个,服装和化妆品也有很多种,很多个。因为文化、环境、个人等因素的存在,所以着装打扮也不尽相同。一个人可以穿休闲服,可以带上一个墨镜,如果下雨的话,还可以带上一个雨伞,或穿上一件雨衣。这些都可以形容为是一种装饰,它灵活。一个人可以穿多种多样的衣服,可以画很多不同的妆(只要想),那么说明装饰是可扩展的,它扩展的是一个人的外在表现。当然也可以添加一块手表,一个墨镜,那么它可以看时间,可以遮挡强光(还可以改变外貌,使人更酷),那么现在就扩展了人的行为:可以看时间,可以在强光下长时间看。

如果是冬天,天气寒冷,那么人可以穿保暖性强的服装,如棉服;如果是夏天,天气炎热,那么可以穿薄点的,通气性好的服装,如Tshirt,或不穿。

环顾周围,可以发现很多对象,而且很容易的说出它们的属性,例如:杯子,房子,鼠标,笔,手机等。但把现实世界的对象抽象为软件中的类型和对象时,会发现,并不容易。面向对象的设计与现实世界的对象是映射的。那么现实中解决问题的方法和面向对象的设计中的各种原则,例如设计模式也是对应的。如果在面向对象的软件设计和开发过程中,把两者联系起来,会发现,一切都是那么的合情合理。

现在描述一个场景:一个有老师,工程师,小学生的环境下(话剧的化妆间),每个人的穿着不一样,有穿学生装的,有带电脑的,有带领带的等。他们都要通过化妆师来化妆打扮以适合各自的角色,假设他们都穿着内衣来进行化妆。那么学生要穿:学生装,红领巾,短裤;老师要穿:长衣衫,长裙,平底鞋,眼镜;工程师:tshirt,裤子,皮鞋,带电脑,带领带。

在化妆间,发现,这些学生装,红领由,电脑,眼镜等都是独自存在的,而不是来就带有的(假设)。那到哪个角色,就可以为这个角色以角色的特征来添加相应的衣服和配套用品。

穿是人的行为,老师,小学生,工程师都能执行这个行为。

装饰者的装饰对应的功能是主要和次要类中所共有的,在这个例子中,他们有共同的行为:穿。然后被装饰对象和装饰对象拥有相同的抽象,也就是具有同一个父类,那么才能可能利用多态来实现装饰(包装)。装饰者模式有一个要素:

被装饰者(主要的,人,小学生,工程师,老师)和装饰者(各种服装,配套用品,皮鞋,电脑,眼镜等)共同的接口:这里就是人,精确的说是人的行为:穿。而为这个穿行为,主要者:各种职业有基本的穿着行为。然后通过装饰各种次要者,来实现各种角色服饰的目的。其中服装和配套用品可以有很多种,它是灵活的。可以要哪种服装加哪种。

(一)基类:其中有主要和次要的共同接口行为:Dress

public abstract class Person
{
    
public void Display()
    {
        Dress();
    }
    
public abstract void Dress();
}

(二)装饰者

从基类继承,且类中有要装饰的对象:

public abstract class Decorator : Person
{
    
protected Person _person;
    
public Decorator(Person person)
    {
        _person 
= person;
    }
    
public override void Dress()
    {
        
throw new NotImplementedException();
    }
}

(三)装饰者实现

实现装饰者,做为各种服装,配套类。如长裙类。

public class Longuette : Decorator
{
    
public Longuette(Person person):base(person)
    { } 

    
public override void Dress()
    {
        _person.Dress();
        Console.WriteLine(
"longuette");
    }
}

(四)被装饰实现

主要的实现,例如学生

public class Student : Person
{
    
public override void Dress()
    {
        Console.WriteLine(
"student's");
    }
}

 

使用:

Person person = new Student();
TShirt t 
= new TShirt(person);
t.Display();

 

装饰者会把被装饰者包装起来。这个包装的过程就是在装饰者中保留了对父类的一个的引用,同时装饰者继承了父类,当对一个从父类派生的被装饰者进行装饰时,可以调用这个引用的功能,同时对同一功能来说,还要加上装饰者本身的这个行为,做为一种装饰存在。实现了功能的附加。

装饰者模式的前提是装饰者和被装饰者具有相同的父类型。

它的定义是这样的:定义一个共同的父类,然后定义一个装饰抽象类,从这个父类派生。这个装饰抽象类中,保留一个父类的实例引用。定义一个被装饰者,也从这个父类派生。

当使用时,把被装饰者实例传递到装饰者,从另一形式上说,装饰者包起了被装饰者的实例,那么在执行行为时,可以在执行派生的行为外,还能通过保留的实例引用使用被包装对象的这个行为。这样就完成了职责的附加,扩展了功能。

现在考虑一个场景:设计一块电子表,它有最基本的计时功能。然后,为了用于各种不同的用途,例如:潜水用,森林里用,黑夜用等,可以为它添加不同的接口行为。可以添加指南针功能,可以增加防水功能,可以增加夜视灯功能。它的可附加功能有很多,且各个附加功能可能多个使用。在这种情况下,可以设计为装饰模式。

首先,要抽象出主要和次要的共同父类,这里把电子表做为这个父类,且这个父类具有最基本的计时能力。

public class Watch
{
    
public virtual void Capability()
    {
        Console.WriteLine(
"计时");
    }
}

其次,设计装饰抽象,把父类引用保留在这个类中,同时在构造时把被装饰者传给这个引用,以调用这个被装饰者的能力。

 

public abstract class WatchDecorator : Watch
{
    
protected Watch _watch;
    
public WatchDecorator(Watch watch)
    {
        _watch 
= watch;
    }
    
public override void Capability()
    {
        _watch.Capability();
    }
}

然后设计多个装饰实现者,实现要附加的功能:防水,夜视,指南针。

public class Waterproof : WatchDecorator
{
    
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 watch = new Watch();
watch.Capability();
Console.WriteLine(
"****************************");
//添加夜视和指南功能
watch = new NightSight(watch);
watch 
= new Compass(watch);
watch.Capability();

 

装饰,或其他模式,或面向对象的设计的原则等等,如果与真实世界联系起来,会发现它们一切都在情理之中,且有的或有时比真实的世界更理想。好比艺术,来源于生活,但高于生活。同样,这些优秀的经验来自于真实的世界,且有时还高于这个世界。