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())。。。
于是,我又额外花了一些时间来找到这个问题,修改它并在下次添加新类的时候战战兢兢提醒自己不要犯这种低级的错误。那么,我们有没有好的办法来解决这个问题呢?

问题分析

其实就算每次添加新类的时候我们都能仔细的小心避免维护相同的逻辑,这段代码的设计也还是有可以改进的地方,比如,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.

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

类图如下:

这完全符合我们的需求,让我们试着修改我们的代码。

使用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模式的妙用。

到此这篇关于C#中的TemplateMethod模式问题分析的文章就介绍到这了,更多相关C# TemplateMethod模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++设计模式之模板方法模式(TemplateMethod)

    模板方法模式使用继承来实现模式的功能,在基类使用一个方法来定义算法的各个步骤,这些步骤(方法)的具体实现会放到子类中,通过这样来实现不同算法对象的算法拼合,完成该对象整体算法的实现. 作用 模板方法中定义了具体操作中所使用算法的各个步骤,并将其实现交由子类完成,从而实现多种不同的功能: 类视图 实现 class Lunch { public: Lunch(){} virtual ~Lunch(){} void feed() { cooking(); eating(); cleaning(); }

  • C#中的TemplateMethod模式问题分析

    一个真实的故事 大学的时候就开过一门课程,讲设计模式,可是大学生没什么编程实践经验,在大学里面听设计模式的感觉,就像听天书.听着都有道理,可是完全领会不到其中的奥妙,大抵原因就在于没有走过弯路,没有吃过设计不当的亏.古人云,"操千曲而后晓声,观千剑而后识器",诚不欺我. 博主在之前的某个项目中,设计出了一些工具类,像属性窗口,错误提示窗口,还有一个窗口管理类管理它们,当时我实现工具保存时候的代码是这样的: class WindowManager { private List<IT

  • Python编程中的反模式实例分析

    本文实例讲述了Python编程中的反模式.分享给大家供大家参考.具体分析如下: Python是时下最热门的编程语言之一了.简洁而富有表达力的语法,两三行代码往往就能解决十来行C代码才能解决的问题:丰富的标准库和第三方库,大大节约了开发时间,使它成为那些对性能没有严苛要求的开发任务的首选:强大而活跃的社区,齐全的文档,也使很多编程的初学者选择了它作为自己的第一门编程语言.甚至有国外的报道称,Python已经成为了美国顶尖大学里最受欢迎的编程入门教学语言. 要学好一门编程语言实属不易,在初学阶段,就

  • js正则表达式中的单行模式与多行模式实例分析

    本文实例分析了js正则表达式中的单行模式与多行模式.分享给大家供大家参考.具体如下: js正则表达式是不支持单行模式的.也就是说,不能把某段内容(有换行),采用模式修正符来处理,使整段内容当成一行来处理. 正则多行替换,需要添加/mg模式增强符. <html> <head> <script type="text/javascript"> //正则替换链接,链接有换行 function t1(){ var con = document.getEleme

  • php中注册器模式类用法实例分析

    本文实例讲述了php中注册器模式类用法.分享给大家供大家参考,具体如下: 注册器读写类 Registry.class.php <?php /** * 注册器读写类 */ class Registry extends ArrayObject { /** * Registry实例 * * @var object */ private static $_instance = null; /** * 取得Registry实例 * * @note 单件模式 * * @return object */ pu

  • 理解javascript中的MVC模式

    MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法.模型有对数据直接访问的权利.模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作. 视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面.当然也包括一些事件的注册或者ajax请求操作(发布事件),都是放在视图

  • 深入理解Android中的建造者模式

    前言 在Android开发过程中,我发现很多安卓源代码里应用了设计模式,比较常用的有适配器模式(各种adapter),建造者模式(Alert Dialog的构建)等等.虽然我们对大多数设计模式都有所了解,但是在应用设计模式的这个方面,感觉很多人在这方面有所不足.所以这篇文章我们一起深入的理解Android中的建造者模式. 建造者模式(Builder Pattern)也叫生成器模式,其定义如下: separate the construction of a complex object from

  • 深入解析C#设计模式编程中对建造者模式的运用

    示例 我们先来以这样一个场景引入:  在电脑城装机总有这样的经历.我们到了店里,先会有一个销售人员来询问你希望装的机器是怎么样的配置,他会给你一些建议,最终会形成一张装机单.和客户确定了装机配置以后,他会把这张单字交给提货的人,由他来准备这些配件,准备完成后交给装机技术人员.技术人员会把这些配件装成一个整机交给客户. 不管是什么电脑,它总是由CPU.内存.主板.硬盘以及显卡等部件构成的,并且装机的过程总是固定的: 把主板固定在机箱中 把CPU安装到主板上 把内存安装到主板上 把硬盘连接到主板上

  • 举例讲解设计模式中的访问者模式在Java编程中的运用

    访问者(Visitor)模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作.访问者模式的结构图如下: 通过上图可以看到他有如下角色: 抽象访问者(Visitor)角色:定义接口,声明一个或多个访问操作. 具体访问者(ConcreteVisitor)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作. 抽象元素(Visitable)角色:声明一个接受操作,接受一个访问者对象作为一个参数. 具体元素结点(Concret

  • 详解C#设计模式编程中的模板方法模式使用

    一.引言 提到模板,大家肯定不免想到生活中的"简历模板"."论文模板"."Word中模版文件"等,在现实生活中,模板的概念就是--有一个规定的格式,然后每个人都可以根据自己的需求或情况去更新它,例如简历模板,下载下来的简历模板的格式都是相同的,然而我们下载下来简历模板之后我们可以根据自己的情况填充不同的内容要完成属于自己的简历.在设计模式中,模板方法模式中模板和生活中模板概念非常类似,下面让我们就详细介绍模板方法的定义,大家可以根据生活中模板的概

  • 基于Java 生产者消费者模式(详细分析)

    生产者消费者模式是多线程中最为常见的模式:生产者线程(一个或多个)生成面包放进篮子里(集合或数组),同时,消费者线程(一个或多个)从篮子里(集合或数组)取出面包消耗.虽然它们任务不同,但处理的资源是相同的,这体现的是一种线程间通信方式. 本文将先说明单生产者单消费者的情况,之后再说明多生产者多消费者模式的情况.还会分别使用wait()/nofity()/nofityAll()机制.lock()/unlock()机制实现这两种模式. 在开始介绍模式之前,先解释下wait().notify()和no

随机推荐