我假设看这篇文章的朋友对装饰者模式都能有各自的、深入的理解。因为这篇文章是讨论装饰者模式的性能问题。
在本人的“.NET简谈设计模式之(装饰者模式)”一文中比较详细的讲解了装饰者模式的一般应用,但是我总是感觉装饰者模式隐隐约约之中有点不完美。经过我昨天一整天的思考、推敲终于找到了它隐隐约约中的那点不完美是什么,为了行为去继承带来的无辜的性能开销。所以本人想把它写出来,跟大家讨论下装饰者模式的性能该如何平衡。是用时间换空间还是用空间换时间,这里的时间就是我们开发的效率时间。
首先回顾一下装饰者模式诞生的本意是什么,它的官方意思是:动态地给一个对象添加一些额外的职责。我们都知道给对象扩展功能是通过继承来实现,但是继承有它的不好之处,比如:子类与父类之间的耦合、子类的无限扩大等等。而装饰者模式就是想利用动态的给需要扩展的对象添加功能。将需要扩展的动能独立起来,作为一个个装饰类,在需要的时候给对象穿上这个装饰。
1:
这张类图照这个样子发展下去不得了,子类无限膨胀,后面需求谁都不知道。这是我们一般扩展对象的正常方法,我们来看一下装饰者模式的原型。
2:
将需要扩展的功能独立起来,当需要的时候动态的添加功能。我想这就是装饰者名称由来,将后期扩展的功能比喻成装饰者,是很形象。
但是当我们带着这张图的原理去看代码的时候,它的结构根本不是这样的“干净”。所以说理论与实践是分不开的。请看代码:
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace ConsoleApplication2
- {
- public class ConcreteConpontent
- {
- public virtual void Operation()
- {
- Console.WriteLine("顶级待装饰对象");
- }
- public virtual void Message()
- {
- Console.WriteLine("顶级对象消息");
- }
- }
- public abstract class Decorator : ConcreteConpontent
- {
- protected ConcreteConpontent m_compontent;
- public void SetCompontent(ConcreteConpontent com)
- {
- m_compontent = com;
- }
- }
- public class ConcreteDecoratorA : Decorator
- {
- public override void Operation()
- {
- m_compontent.Operation();
- Console.WriteLine("ConcreteDecoratorA进行了方法的动态添加");
- }
- public override void Message()
- {
- m_compontent.Message();
- Console.WriteLine("ConcreteDecoratorA进行了Message方法的动态添加");
- }
- }
- public class ConcreteDecoratorB : Decorator
- {
- public override void Operation()
- {
- m_compontent.Operation();
- Console.WriteLine("ConcreteDecoratorB进行了方法的装饰");
- }
- public override void Message()
- {
- m_compontent.Message();
- Console.WriteLine("ConcreteDecoratorB进行了Message方法的动态添加");
- }
- }
- public class ConcreteDecoratorC : Decorator
- {
- public override void Operation()
- {
- m_compontent.Operation();
- Console.WriteLine("ConcreteDecoratorC进行了方法的装饰");
- }
- public override void Message()
- {
- m_compontent.Message();
- Console.WriteLine("ConcreteDecoratorC进行了Message方法的动态添加");
- }
- }
- }
装饰者模式的基本代码原型差不多就这样子的。当我看到装饰者模式是这样的一个代码结构的时候,其实说心里话我难受。里面不是带着继承吗?为什么要继承,心理面不忍发了点牢骚。ConcreteConpontent是被装饰者对象,首先我们要确定要扩展的对象是可以让我们扩展的。其实我知道继承是为了拿到要扩展对象的行为,并且标示所有的装饰者是属于一种类型的,在使用的时候就可以用基类来使用所有的装饰者。如果没有继承显然是不能用基类进行统一调用的,继承还有一个作用就是为了拿到被装饰者的行为,用它的为操作不同的实例,是够聪明的。
我假如我不需要用基类进行统一调用装饰者,我是否就可以不继承自被装饰者了;为了能够实现装饰者的无限递增的装饰,我对代码进行了简单的修改,请看代码:
- using System;
- namespace ConsoleApplication1
- {
- public class ConcreteConpontent
- {
- public virtual void Operation()
- {
- Console.WriteLine("顶级待装饰对象");
- }
- public virtual void message()
- {
- Console.WriteLine("顶级对象消息");
- }
- }
- public abstract class Decorator
- {
- private ConcreteConpontent m_compontent;
- protected Decorator decorator;
- public void SetCompontent(ConcreteConpontent com, Decorator de)
- {
- m_compontent = com;
- decorator = de;
- }
- public void SetCompontent(ConcreteConpontent com)
- {
- m_compontent = com;
- }
- public virtual void Operation()
- {
- if (decorator != null)
- decorator.Operation();
- else
- m_compontent.Operation();
- }
- public virtual void message()
- {
- if (decorator != null)
- decorator.message();
- else
- m_compontent.message();
- }
- }
- public class ConcreteDecoratorA : Decorator
- {
- public override void Operation()
- {
- base.Operation();
- Console.WriteLine("ConcreteDecoratorA进行了方法的装饰");
- }
- public override void message()
- {
- base.message();
- Console.WriteLine("ConcreteDecoratorA进行了message方法的动态添加");
- }
- }
- public class ConcreteDecoratorB : Decorator
- {
- public override void Operation()
- {
- base.Operation();
- Console.WriteLine("ConcreteDecoratorB进行了方法的装饰");
- }
- public override void message()
- {
- base.message();
- Console.WriteLine("ConcreteDecoratorB进行了message方法的动态添加");
- }
- }
- public class ConcreteDecoratorC : Decorator
- {
- public override void Operation()
- {
- base.Operation();
- Console.WriteLine("ConcreteDecoratorC进行了方法的装饰");
- }
- public override void message()
- {
- base.message();
- Console.WriteLine("ConcreteDecoratorC进行了message方法的动态添加");
- }
- }
- }
如果我们这是想扩展一个简单的小功能,让我们继承一个很大的对象是不是有点不划算。只是想用被装饰者的行为,去操作装饰者原型实例。我们可以牺牲一下代码的冗余来解决这个性能问题。书上对继承的解释是用来避免手动输入被装饰者的行为代码。我觉得这点根本没有说服力。其实装饰者模式是想动态的给对象添加功能、行为、职责。在使用的时候还是想通过被装饰者进行引用所有的装饰者实例,这样才是继承最有效的说服力。不继承我一样可以有同样的行为、一样可以实现无限递增的嵌套装饰者实例。要想实例套实例,那么他们必须来自同一个祖先,同样是装饰者,要想让装饰者套装饰者,那么在装饰者的类中需要有一个对装饰者类型的引用,但是每一个装饰者不可能一样。所以必须让他们继承同一个基类才行,后面再多的装饰者只要继承同一个基类那么就可以互相引用。
总结:在我们选择使用装饰者模式的时候,需要根据自己的使用情况进行适当修改。在没有必要的情况下不需要继承那么大的一个对象。
本文转自 王清培 51CTO博客,原文链接:http://blog.51cto.com/wangqingpei557/622150,如需转载请自行联系原作者