装饰模式

姓名:高振松

学号:07770116

1、生活场景:看过美国科幻片的同学应该都会为美国人那惊人的想象力说折服吧。在美国的科幻片里有一种东西会经常在镜头里出现,那就是会飞行的交通工具(汽车,卡车之类的)。这些交通工具既可以在陆地上跑,也可以在天空中飞行或者在水中行驶。现假设,现在有一家汽车生产商,最近突破一项关键技术,使得所有类型的车辆都可以在天空中飞或者在水中行驶的功能。而你正好在给该生产商开发系统软件,现在要实现对所有车辆的模拟,你该如何进行编写。

2、我在这里展示我的不假思索的解决方案:

我记得在以前上面向对象的编程语言课时,老师在讲解类的继承特性时提到使用类的继承可以来扩展类的功能。在一般直觉下,我首先想到的是用类的继承关系来模拟上述问题。

类图如下: 私家车可以飞,卡车则可以在水中行驶。

实现代码:

私家车的基本功能:
public class _FutureCar
    {
        public virtual string OperateA()
        {
            return "小型私家车在陆地上以一档速度行驶。";
        }

        public virtual string OperateB()
        {
            return "小型私家车在陆地上以二档速度行驶。";
        }

        public virtual string OperateC()
        {
            return "小型私家车在陆地上以三档速度行驶。";
        }

}	
私家车的扩展功能:
public class _FlyableFutureCar : _FutureCar
    {
        public override string OperateA()
        {
            string strDo = base.OperateA();
            strDo += "\n 在启动飞行功能在空中飞行!";
            return strDo;
        }

        public override string OperateB()
        {
            string strDo = base.OperateB();
            strDo += "\n 在启动飞行功能在空中飞行!";
            return strDo;
        }

        public override string OperateC()
        {
            string strDo = base.OperateC();
            strDo += "\n 在启动飞行功能在空中飞行!";
            return strDo;
        }
    }

卡车的基本功能:
public class _FutureTruck
    {
        public virtual string OperateA()
        {
            return "大卡车在陆地上以一档速度拉货。";
        }

        public virtual string OperateB()
        {
            return "大卡车在陆地上以二档速度拉货。";
        }

        public virtual string OperateC()
        {
            return "大卡车在陆地上以三档速度拉货。";
        }
    }
卡车的扩展功能:
public class _DriveInWaterFutureTruck : _FutureTruck
    {
        public override string OperateA()
        {
            string strDo = base.OperateA();
            strDo += "\n 在启动水中行驶功能在水中行驶!";
            return strDo;
        }

        public override string OperateB()
        {
            string strDo = base.OperateB();
            strDo += "\n 在启动水中行驶功能在水中行驶!";
            return strDo;
        }

        public override string OperateC()
        {
            string strDo = base.OperateC();
            strDo += "\n 在启动水中行驶功能在水中行驶!";
            return strDo;
        }
    }
3、现在就让我们开探讨下这种方案中的缺陷。

         情形一: 如果生产商要生产可以在水中行驶的Car,和可以在空中飞行的卡车,则该怎么改代码?在这里唯一的方式则是添加新的类 DriveInWaterCar ,和 FlyableTruck。

这里的车辆和扩展类。功能还比较少,要是10种类型的车辆和10种类型的扩展功能的话,进行组合将可能会产生110个。

         情形二:如果要动态地添加车辆和功能的话,将这样改代码?在这个情形中,按照继承的方式,删除的功能,只需要删除派生类即可。要是删除车辆的一个类别(如Truck),则要删除大量的代码,删除_FutureTruck, 及其派生类_DriveInWaterFutureTruck, FlyableTruck。对于添加,存在相同问题。也就是说,用一般的继承关系来处理上述问题,存在扩展不够开放问题。

4、归纳阶段
下面是我用装饰模式来解决类继承带来的缺陷。
在这展示装饰模式之前,我们先了解装饰模式的目的或者是其所针对的问题。
首先,装饰(Decorator)模式又名包装(Wrapper)模式[GOF95]。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。注意,装饰模式是继承关系的一个替代方案,实现与继承关系相同的效果。
其次,装饰模式,只是以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。就像西游记里的孙猴子一样,任它72变变来变去,本质上还是个猢狲。
5、验证阶段
 好的,现在让我们来看看装饰模式是否解决了之前不假思索的缺陷。
1.将车辆(被装饰者)和功能(装饰者)进行组合将产生大量类问题。同样是10个车辆类型(ConcreteComponent)和10个功能(ConcreteDecorator)进行组合,将产生20具体类,1个抽象类,1个接口。与之前30个相比减少了8个。这是因为装饰类(FlyComponent或者DriveInWaterComponent)只是引用了被装饰类(FutureCar或者FutureTruck),而且执行的都是相同接口定义的行为,所以装饰类(FlyComponent或者DriveInWaterComponent)可以随意更换被装饰类(FutureCar或者FutureTruck),这就减少了类的产生。

 

2.扩展不够开放问题。由于装饰模式中,装饰类(FlyComponent或者DriveInWaterComponent)只是引用了被装饰类(FutureCar或者FutureTruck),同样对扩展也开放。添加一个新的被装饰类,不用被装饰类继承就可以被装饰。添加一个装饰类,不用继承被装饰类,就可以进行装饰被装饰类。

相关代码:
接口IComponent:
public interface IComponent
    {

        string OperateA();

        string OperateB();

        string OperateC();
}
被装饰类FutureCar 和 FutureTruck:
public class FutureTruck : IComponent
    {
        #region IComponent 成员
        public string OperateA()
        {
            return "大卡车在陆地上以一档速度在拉货。";
        }

        public string OperateB()
        {
            return "大卡车在陆地上以二档速度在拉货。";
        }

        public string OperateC()
        {
            return "大卡车在陆地上以三档速度在拉货。";
        }
        #endregion
    }
public class FutureCar : IComponent
    {
        #region IComponent 成员
        public string OperateA()
        {
            return "小型私家车在陆地上以一档速度行驶。";
        }

        public string OperateB()
        {
            return "小型私家车在陆地上以二档速度行驶。";
        }

        public string OperateC()
        {
            return "小型私家车在陆地上以三档速度行驶。";
        }
        #endregion
    }


装饰类:
public abstract class Decorate : IComponent
    {
        protected IComponent m_COM = null;
        public abstract IComponent Component
        {
            set;
        }
        #region IComponent 成员
        public abstract string OperateA();
        public abstract string OperateB();
        public abstract string OperateC();
        #endregion
    }	

被装饰类:
public class FlyComponent : Decorate
    {

        public override IComponent Component
        {
            set
            {
                if (value is FutureCar)
                {
                    base.m_COM = value;
                }
                else
                {
                    throw new Exception("Component 不是 FutureCar类型!");
                }
            }
        }

        public override string OperateA()
        {
            string strDo = base.m_COM.OperateA();
            strDo += "\n 在启动飞行功能在空中飞行!";
            return strDo;
        }

        public override string OperateB()
        {
            string strDo = base.m_COM.OperateB();
            strDo += "\n 在启动飞行功能在空中飞行!";
            return strDo;
        }

        public override string OperateC()
        {
            string strDo = base.m_COM.OperateC();
            strDo += "\n 在启动飞行功能在空中飞行!";
            return strDo;
        }
    }

public class DriveInWaterComponent : Decorate
    {
        public override IComponent Component
        {
            set
            {
                if (value is FutureTruck)
                {
                    base.m_COM = value;
                }
                else
                {
                    throw new Exception("Component 不是 FutureTruck类型!");
                }
            }
        }
        public override string OperateA()
        {
            string strDo = base.m_COM.OperateA();
            strDo += "\n 在启动水中行驶功能在水中行驶!";
            return strDo;
        }

        public override string OperateB()
        {
            string strDo = base.m_COM.OperateB();
            strDo += "\n 在启动水中行驶功能在水中行驶!";
            return strDo;
        }

        public override string OperateC()
        {
            string strDo = base.m_COM.OperateC();
            strDo += "\n 在启动水中行驶功能在水中行驶!";
            return strDo;
        }
    }

6、总结
装饰模式的缺点:
由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。使用条件:

在以下情况下应当使用装饰模式:

  1. 需要扩展一个类的功能,或给一个类增加附加责任。
  2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。

 

参考资料:《C# 设计模式》 [美] Jame W.Cooper著 电子工业出版社。

 

posted @ 2010-12-01 21:49  天津城建学院软件工程  阅读(577)  评论(0编辑  收藏  举报