C#中的TemplateMethod模式

一个真实的故事

大学的时候就开过一门课程,讲设计模式,但是大学生没什么编程实践经验,在大学里面听设计模式的感受,就像听天书。听着都有道理,但是彻底领会不到其中的奥妙,大抵缘由就在于没有走过弯路,没有吃过设计不当的亏。古人云,“操千曲然后晓声,观千剑然后识器”,诚不欺我。
 
博主在以前的某个项目中,设计出了一些工具类,像属性窗口,错误提示窗口,还有一个窗口管理类管理它们,当时我实现工具保存时候的代码是这样的:

编程

class WindowManager
    {
        private List<ITool> _Tools = new List<ITool>();        

        public void AddTool(ITool tool)
        {
            _Tools.Add(tool);
        }

        public void SaveAllTools()
        {
            foreach(var tool in _Tools)
            {
                tool.Save();
            }
        }
    }

    interface ITool
    {
        bool BeforeSave();
        void Save();
        void AfterSave();
    }

    class PropertyWindow : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

    class ErrorLis : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

当时博主对这段代码还挺满意,彻底没有看出这儿有什么问题,以为这简直写的太OO了,有类,有接口,有针对接口编程,至于新加的工具类,也不会影响原来的代码,简直太符合开闭原则了。老铁,没毛病!
 
好日子就这么继续下去,每当须要新添加一个工具,我就新加一个类,在类里面实现Save的逻辑,直到有一天,添加了一个ResourceControl

设计模式

class ResourceControl : ITool
    {
        public bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        public void Save()
        {
            if (!BeforeSave())
            {
                //do save
                AfterSave();
            }
        }

        public void AfterSave()
        {

        }
    }

 
在它的save里面,我把if(BeforeSave())写成了if(!BeforeSave())。。。
因而,我又额外花了一些时间来找到这个问题,修改它并在下次添加新类的时候战战兢兢提醒本身不要犯这种低级的错误。那么,咱们有没有好的办法来解决这个问题呢?

ide

问题分析

其实就算每次添加新类的时候咱们都能仔细的当心避免维护相同的逻辑,这段代码的设计也仍是有能够改进的地方,好比,BeforeSave和AfterSave在这里做为接口ITool的一部分而公开,意味着客户代码能够自由的调用BeforeSave和AfterSave,然而这极可能并非代码做者的本意,毕竟,不调用Save而单独调用BeforeSave和AfterSave有什么意义呢?让客户可以看到更多没必要要的方法,增长了客户错误使用接口的可能性,不是么?
 
综上所述,咱们须要解决的问题以下:

工具

  • 抽象出Save, BeforeSave和AfterSave的逻辑关系,在一个地方固定下来,确保新增长的类所实现的这三个方法,都能自动具备这种逻辑关系。
  • 对客户代码隐藏没必要要的接口。
     
    这种场景下面,咱们须要用到设计模式中的TemplateMethod(模版方法)模式。
     


TemplateMethod模式

WIKI上面,TemplateMethod模式的定义以下,
In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm's structure.
url

大概意思就是,模版方法模式是一种行为类设计模式,容许软件在更高的层次定义程序骨架,可是能够在子类推迟实现某些步骤。
 
类图以下:



spa

这彻底符合咱们的需求,让咱们试着修改咱们的代码。
 
.net

使用TemplateMethod从新实现的代码

class WindowManager
    {
        private List<AbstractTool> _Tools = new List<AbstractTool>();        

        public void AddTool(AbstractTool tool)
        {
            _Tools.Add(tool);
        }

        public void SaveAllTools()
        {
            foreach(var tool in _Tools)
            {
                tool.Save();
            }
        }
    }

    abstract class AbstractTool
    {
        protected abstract bool BeforeSave();
        protected abstract void DoSave();
        protected abstract void AfterSave();
        public void Save()
        {
            if(!BeforeSave())
            {
                DoSave();
                AfterSave();
            }

        }        
    }

    class PropertyWindow : AbstractTool
    {
        protected override bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        protected override void DoSave()
        {
            
        }

        protected override void AfterSave()
        {

        }
    }

    class ErrorLis : AbstractTool
    {
        protected override bool BeforeSave()
        {
            //do something specific here
            return true;
        }

        protected override void DoSave()
        {

        }

        protected override void AfterSave()
        {

        }
    }

从上面咱们能够看到,咱们用一个抽象类AbstractTool代替以前的ITool接口,抽象类和接口的一个区别就是,抽象类能够在其中嵌入某些逻辑,因此咱们在Save这个公共的非虚方法中,彻底实现了咱们的BeforeSave和AfterSave逻辑,仅仅留下了BeforeSave,AfterSave和DoSave给子类覆盖。这样咱们获得的好处是:设计

  • 抽象类只公开了一个Save方法,因此客户代码不用担忧会调用其余错误的方法。
  • 抽象类彻底固定了Save逻辑,先调用BeforeSave检查,以后执行DoSave进行具体的Save事项,最后进行AfterSave行为。子类只须要从新依据子类的需求覆盖这三个虚方法便可。新添加的工具类,只要覆盖这三个虚方法,至于虚方法之间的逻辑,抽象类已经固定,不用担忧。

结论

“纸上得来终觉浅,绝知此事要躬行”,祖宗的话,不会错的,若是没有必定的编程实践和总结,是没有办法领悟设计模式的,博主也是经过以前那个例子才领悟到TemplateMethod模式的妙用。但愿你们多多编程,早日领悟。3d

相关文章
相关标签/搜索