设计模式学习笔记(十)——Decorator装饰模式

Decorator装饰模式是一种结构型模式,它主要是解决:“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。继承为类型引入的静态特质的意思是说以继承的方式使某一类型要获得功能是在编译时。所谓静态,是指在编译时;动态,是指在运行时。

GoF《设计模式》中说道:动态的给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。

下面来看看Decorator模式的结构:



看这个结构好像不是很明白,下面我根据代码讲解一下这个结构。我想了一个场景:我们现在用的手机功能很多,我就用Decorator模式实现一下对某个手机的GSP和蓝牙功能扩展。

首先,我们需要一个手机的接口或者是抽象类,我这里就用抽象类来实现,代码如下:

public abstract class AbstractCellPhone

    {

        public abstract string CallNumber();

        public abstract string SendMessage();

}

AbstractCellPhone也就是结构图中的Component,然后,我再来实现NokiaMoto的手机类,这类要继承AbstractCellPhone,也就是图中ConcreteComponent类要继承Component,实现代码如下:

public class NokiaPhone : AbstractCellPhone

    {

        public override string CallNumber()

        {

            return "NokiaPhone call sombody";

        }

 

        public override string SendMessage()

        {

            return "NokiaPhone send a message to somebody";

        }

    }

 

    public class MotoPhone : AbstractCellPhone

    {

        public override string CallNumber()

        {

            return "MotoPhone call sombody";

        }

 

        public override string SendMessage()

        {

            return "MotoPhone send a message to somebody";

        }

 }   

接下来我需要一个Decorator接口或者抽象类,实现代码如下:

public abstract class Decorator:AbstractCellPhone

    {

        AbstractCellPhone _phone;

 

        public Decorator(AbstractCellPhone phone)

        {

            _phone = phone;

        }

 

        public override string CallNumber()

        {

            return _phone.CallNumber();

        }

 

        public override string SendMessage()

        {

            return _phone.SendMessage();

        }

  }

正如结构图中,这个Decorator即继承了AbstractCellPhone,又包含了一个私有的AbstractCellPhone的对象。这样做的意义是:Decorator类又使用了另外一个Component类。我们可以使用一个或多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。在下来,我要实现GSP和蓝牙的功能扩展,它们要继承自Decorator,代码如下:

public class DecoratorGPS : Decorator

    {

        public DecoratorGPS(AbstractCellPhone phone)

            : base(phone)

        { }

 

        public override string CallNumber()

        {

            return base.CallNumber() + " with GPS";

        }

 

        public override string SendMessage()

        {

            return base.SendMessage() + " with GPS";

        }

    }

 

    public class DecoratorBlueTooth : Decorator

    {

        public DecoratorBlueTooth(AbstractCellPhone phone)

            : base(phone)

        { }

 

        public override string CallNumber()

        {

            return base.CallNumber() + " with BlueTooth";

        }

 

        public override string SendMessage()

        {

            return base.SendMessage() + " with BlueTooth";

        }

 }

最后,用客户端程序验证一下:

static void Main(string[] args)

        {

             AbstractCellPhone phone = new NokiaPhone();

            Console.WriteLine(phone.CallNumber());

            Console.WriteLine(phone.SendMessage());

            DecoratorGPS gps = new DecoratorGPS(phone);     //add GSP

            Console.WriteLine(gps.CallNumber());

            Console.WriteLine(gps.SendMessage());

            DecoratorBlueTooth bluetooth = new DecoratorBlueTooth(gps); //add GSP and bluetooth

            Console.WriteLine(bluetooth.CallNumber());

            Console.WriteLine(bluetooth.SendMessage());

            Console.Read();

     }

执行结果:

NokiaPhone call sombody

NokiaPhone send a message to somebody

NokiaPhone call sombody with GPS

NokiaPhone send a message to somebody with GPS

NokiaPhone call sombody with GPS with BlueTooth

NokiaPhone send a message to somebody with GPS with BlueTooth

 

从执行的结果不难看出扩展功能已被添加。最后再说说Decorator装饰模式的几点要点:

1、通过采用组合、而非继承的手法,Decorator模式实现了在运行时动态的扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。

2Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。

3Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。(在这里我想谈一下我的理解:当我们实例化一个Component对象后,要给这个对象扩展功能,这时我们把这个Component对象当作参数传给Decorator的子类的构造函数——也就是扩展方法的功能类。对于引用类型传参时,实际上只是传递对象的地址,这样,在功能扩展是,操作的应该是同一个对象)

4Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。Decorator是在运行时对功能进行组合。

posted @ 2006-06-26 14:43 KiddLee Views(6816) Comments(10) Edit 收藏

 回复 引用 查看   
#1楼2006-06-26 15:21 | TerryLee      
Decorator模式的核心就是在不改变接口的前提下扩展功能,呵呵:-)
 回复 引用 查看   
#2楼2006-06-26 15:58 | idior      
Lucene的Analyzer就是Decorator的实际教材

http://www.cnblogs.com/kwklover/archive/2006/06/25/435421.html#Feedback

 回复 引用   
#3楼2007-08-20 19:55 | Qian Jin[未注册用户]
Decorator模式跟Bridge模式很像啊,有什么本质上的区别吗?
 回复 引用 查看   
#4楼[楼主]2007-08-21 10:05 | KiddLee      
@Qian Jin
Bridge是将抽象部分与实现部分分离,使他们可以独立的变化
Decorator是动态的给一个对象添加一些额外的职责
在学习设计模式中我有时也有同样的感觉,很多设计模式从代码上看感觉很像,甚至分不清楚。但,是用来应对不同的情况,而且都是本着设计模式原则,设计模式只是一种思想,不应该局限于代码是如何写的,而忽略的真正的精华,如果还是不太清楚,可以看看这个http://www.cnblogs.com/kid-li/archive/2006/11/11/557359.html

 回复 引用   
#5楼2007-08-21 15:00 | Qian Jin[未注册用户]
谢谢你的解答,同意你的观点,正是因为我目前对设计模式不熟,才会在意具体的模式之间的差别,这是一个必须要经历的从具体到抽象的过程。
还有一个问题,jdk中的I/O部分,比如说inputstream类,看样子就是用到了设计模式,我学完桥接模式时认为它用的是桥接,但是学完装饰模式之后感觉应该是装饰模式才对,正确答案是什么?

 回复 引用 查看   
#6楼[楼主]2007-08-22 10:12 | KiddLee      
@Qian Jin
对于jdk,我了解得不多,但是在.Net Framework框架中I/O下的FileStream等类型的确是Decorator模式

 回复 引用   
#7楼2008-05-12 13:48 | 清水无鱼1[未注册用户]
decorator 可以步集成compenet 而只进行组合吗
 回复 引用 查看   
#8楼2011-02-10 16:11 | hanwesley      
请问楼主:问什么装饰模式要用构造方法传入component呢?用set方法注入一样的吧?

如果用set方法,还可以节约一次new?这样不可以吗?

 回复 引用 查看   
#9楼2011-08-05 08:41 | 呼静远      
喜欢你自己的理解
 回复 引用 查看   
#10楼2011-12-03 22:24 | suxiaojack      
#include <stdio.h>
#include <stdlib.h>

typedef struct tagPhone{
char* name;
void (*action)(struct tagPhone*);
} Phone;

typedef void(* PhoneAction)(Phone* phone);

void so()
{
printf("\n");
}

void BaseCall(Phone* p)
{
printf("%s Call sombody",p->name);
}

void withGPS(Phone* p)
{
if(p)
BaseCall(p);
printf(" with gps");
}

void withBlueTooth(Phone* p)
{
if(p)
BaseCall(p);
printf(" with BlueTooth");
}

void withGPSandBlueTooth(Phone* p)
{
withGPS(p);
withBlueTooth(NULL);
}


int main()
{
Phone p;
p.name="nokia phone";
p.action=BaseCall;
p.action(&p);
so();
p.action=withGPS;
p.action(&p);
so();
p.action=withBlueTooth;
p.action(&p);
so();
p.action=withGPSandBlueTooth;
p.action(&p);
so();
return 0;
}
抽象接口会导致代码阅读困难
action的调用形式都一样,谁知道具体实现了什么功能呢!
还是老老实实写函数比较好。