装饰模式作为一个比较常见的模式,我就不再细说了,并且在.net框架中也多次出现(例如全透明装饰类BufferedStream与它的基类Stream)
    此外,另一个比较有趣的例子大家可能不太会用到,这次被装饰的Type这个类型,装饰者是TypeDelegator。
    Type这个类型本身比较复杂,大大小小的实例成员加起来不下100个(包括继承的),其中可覆盖的核心方法有也有50多个。如果需要装饰一下Type的话,那么至少这50多个成员需要被覆盖,而需求可能仅仅只是为了监视某一个属性。这使得手段和目的显得极为不对称。
    面对这样的需求,M$提供了这么一个TypeDelegator类型来作为对Type装饰的基类,具体的装饰类只要再次覆盖其中的几个特定的方法就可以了。
    这时候,应该可以想到,装饰模式中的基础接口应该尽量的轻,像Type这样的重型类似乎不太合适。但是有的时候显然有是需要装饰一下这种重型类。比如说,如果希望在反射某一个Type的时候,当取这个类型的所有实例方法(Type.GetMethod(BindingFlag.Instance))的时候记录一下日志。这样的需求似乎摆明了要用装饰模式。假设没有TypeDelegator类型(或者不知道它的存在),在装饰Type的时候,估计就会火冒三丈的大骂M$,怎么在Type里面放了这么多垃圾方法,或者再找其他的手段来达到这个目的。

    下面进入正题:大接口怎么装饰?
    这里说的大接口(这里的接口是泛意的接口)是指需要覆盖的成员不下20个,而真正需要装饰的成员仅仅是很小一部分。这样要么和M$一样,建一个装饰基类。但是如果仅仅只有一个装饰类,岂不是很浪费。
    那么第二条路是什么?经过漫长的思考,我觉得如果将大接口限定为狭义接口(interface,不包含抽象类等)的话,可以通过Emit技术来生成这么一个动态装饰类。
    问题是:哪些成员需要装饰?这些成员怎么装饰?Debug怎么办?怎样半透明装饰?
    经过否决N个方案后,终于找到了可行的方案。用到的核心技术依然是.net的四大神器:泛型、反射、特性、Emit。其中特性是可选的,通过配置注入也是完全可行的。
    方案的核心手段是建立一个混入类(Mixin),这个混入类的职责是告诉动态装饰工厂如何装饰,并且需要绝对实现半透明的部分。
    举个例子来说吧:
    原始接口:
    public interface ISource
    {
        
void HelloWorld();
        
string Name { getset; }
        
    }

    装饰接口:
    public interface IDecorator
        : ISource
    {
        
event EventHandler NameChanged;
    }

    混入类:
    [DecoratorMixinType(typeof(ISource), typeof(IDecorator))]
    
public class DecoratorMixin
    {

        
private ISource _src;
        
private IDecorator _decorator;

        
public DecoratorMixin(ISource src, IDecoratorForTest decorator)
        {
            _src 
= src;
            _decorator 
= decorator;
        }

        [DecoratorMixinProperty()]
        
public string Name
        {
            
get { return _src.Name; }
            
set
            {
                
if (value != _src.Name)
                {
                    _src.Name 
= value;
                    OnNameChanged(EventArgs.Empty);
                }
            }
        }

        
protected virtual void OnNameChanged(EventArgs e)
        {
            
if (NameChanged != null)
                NameChanged(_decorator, e);
        }

        [DecoratorMixinEvent()]
        
public event EventHandler NameChanged;
    }


    再来看一下ISource的实现:
    public sealed class SrcImpl
        : ISource
    {

        
#region Fields
        
private string _name;
        
        
#endregion

        
#region Ctors

        
public SrcImpl()
        {
            _name 
= this.ToString();
            
        }

        
#endregion

        
#region ISrcForTest Members

        
public void HelloWorld()
        {
            Console.WriteLine(
"Hello world, my name is {0}.", Name);
        }

        
public string Name
        {
            
get { return _name; }
            
set { _name = value; }
        }
        
        
#endregion
    }

    最后,看一下客户端的调用:
    class Program
    {
        
static void Main(string[] args)
        {
            ISource src 
= new SrcImpl();
            IDecorator decorator 
= DecoratorFactory.Create<ISource, IDecorator, DecoratorMixin>(src);
            decorator.HelloWorld();
            decorator.NameChanged 
+= new EventHandler(decorator_NameChanged);
            decorator.Name 
= "Name2";
            decorator.HelloWorld();
            Console.ReadLine();
        }

        
static void decorator_NameChanged(object sender, EventArgs e)
        {
            IDecorator decorator 
= sender as  IDecorator;
            Console.WriteLine(
"I'm \"Program\", some one told me, the source changed it's name to {0}", decorator.Name);
        }
    }


    来看一下最终结果:

Hello world, my name is TestDynamicDecorator.SrcImpl.
I
'm "Program", some one told me, the src changed it's name to New Name
Hello world, my name 
is New Name.

    在这里,魔术师就是DecoratorFactory.Create<ISource, IDecorator, DecoratorMixin>,它负责变出一个匿名类来实现IDecorator,并且将其中的Name属性绑定的方法和NameChanged事件绑定的方法覆盖为调用混入类的方法,而其他方法(例如HelloWorld方法)就直接调用ISource对象的方法。
    (目前这个
DecoratorFactory还未完成,主要是容错方面的问题,完成后将集成到ZForce中一齐发布


posted on 2007-08-14 10:21  Zhenway  阅读(549)  评论(0编辑  收藏  举报