• 装饰模式与大接口的装饰模式的思考


        装饰模式作为一个比较常见的模式,我就不再细说了,并且在.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中一齐发布


  • 相关阅读:
    第三次博客园作业
    centos7+jdk1.8+tomcat8 配置https
    输入30个数存入数组a,求出数的每个位数的平方和存入数组b,从小到大排列后输出(C语言)
    50个[100,300]的随机数,要求用二分法查找从键盘录入的关键数字。找到回复位置,找不到回复不存在(C语言)
    产生20个随机数,在[200,400]内,其中能被5整除的存入数组array2,要求输出array2中的平均值(C语言)
    最小生成树
    PTA路径判断
    PTA构造哈夫曼树
    图的其中两种表示方式
    中序遍历树并判断是否为二叉搜索树
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/854855.html
Copyright © 2020-2023  润新知