当前位置: 首页 > 编程笔记 >

C#中的TemplateMethod模式问题分析

连曜灿
2023-03-14
本文向大家介绍C#中的TemplateMethod模式问题分析,包括了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模式内容请搜索小牛知识库以前的文章或继续浏览下面的相关文章希望大家以后多多支持小牛知识库!

 类似资料:
  • 本文向大家介绍关于c#中单例模式的一些问题,包括了关于c#中单例模式的一些问题的使用技巧和注意事项,需要的朋友参考一下 本文主要介绍了关于单例模式的一些问题,想学习C#单例模式的同学们可以看一看,还是有些帮助 c#中的单例模式 单例模式是指在设计一个类时,保证在运行期间只有一个实例对象,因为过多相同的实例对象会占用内存空间。 ##举个例子 1.声明一个静态的Class1类的变量来引用唯一的对象。

  • 最近,我在这篇文章中读到了关于C#三部分显示格式的内容:https://diptimayapatra.wordpress.com/2014/01/13/3-part-format-of-numbers-in-c/ 3部分格式用于显示正、负和零值数字。字符串格式有三个部分。第一部分对应于阳性试验,第二部分对应于阴性试验。最后一部分用于值为零时。 其结果将为“(+)29.12” 这样做的结果将是“积极

  • 模式定义 表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。 class Visitor; class Element { public: virtual void accept(Visitor& visitor) = 0; //第一次多态辨析 virtual ~Element() {} }; class Eleme

  • 本文向大家介绍C++变位词问题分析,包括了C++变位词问题分析的使用技巧和注意事项,需要的朋友参考一下 在《编程珠玑》一书的第二章提到了一个变位词问题,变位词指的是一个单词可以通过改变其他单词中字母的顺序来得到,也叫做兄弟单词,如army->mary。由变位词可以引申出几个算法问题,包括字符串包含问题,比较两个字符串是否是变位词,以及找出字典中变位词集合的问题。 一、字符串包含问题 (1) 问题描

  • 我有一个简单的案例类: 我正在添加字段“name” java.util.NoSuchelementException:scala.collection.immutable.stream$empt$.head(stream.scala:1104)在scala.collection.immutable.stream$empt$.head(stream.scala:1102)在test.consumer

  • 我正在遵循一个足够简单的YouTube教程(链接在这里),关于使用MVC的存储库设计模式。它很好,但他使用 MVC5 和 EF6,它们对异步方法有很多支持。 我正在使用MVC4,每当我尝试将项目升级为使用EF6时都会遇到重大问题。所以我只使用EF5,但这不是问题。 我将他教程中的代码修改为不使用Async,如下所示(他的原始代码在注释中): 下面是完成一些基本实现后,接口生成的代码(存储库): 同