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模式的更多相关文章
- Java 实现模板方法(TemplateMethod)模式
类图 /** * 业务流程模板.提供基本框架 * @author stone * */ public abstract class BaseTemplate { public abstract voi ...
- 制作类似ThinkPHP框架中的PATHINFO模式功能
一.PATHINFO功能简述 搞PHP的都知道ThinkPHP是一个免费开源的轻量级PHP框架,虽说轻量但它的功能却很强大.这也是我接触学习的第一个框架.TP框架中的URL默认模式即是PathInfo ...
- 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)
在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...
- 4.在MVC中使用仓储模式进行增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-using-the-repository-pattern-in-mvc/ 系列目录: ...
- Qt 中使用Singleton模式需小心
在qt中,使用Singleton模式时一定要小心.因为Singleton模式中使用的是静态对象,静态对象是直到程序结束才被释放的,然而,一旦把该静态对象纳入了Qt的父子对象体系,就会导致不明确的行为. ...
- 安卓中的Model-View-Presenter模式介绍
转载自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0425/2782.html 英文原文:Introduction to M ...
- Objective-C中的委托(代理)模式
我个人更喜欢把委托(Delegate)模式称为代理(Proxy)模式.还是那句话,第一次接触代理模式是在Java中接触的,在Java中实现代理模式和接口是少不了的.当时学习Spring的时候用到了接口 ...
- Android -- 思考 -- 为什么要在项目中使用MVP模式
1,其实有时候一直在找借口不去思考这个问题,总是以赶项目为由,没有很认真的思考这个问题,为什么我们要在项目中使用MVP模式,自己也用MVP也已经做了两个项目,而且在网上也看了不少的文章,但是感觉在高层 ...
- angular中的MVVM模式
在开始介绍angular原理之前,我们有必要先了解下mvvm模式在angular中运用.虽然在angular社区一直将angular统称为前端MVC框架,同时angular团队也称它为MVW(What ...
随机推荐
- 【Spark】不熟悉Spark-shell常用参数?这一张图就够了
- c++(qt)程序员微信群
关注微信公众号"程序员成长日志",回复关键字"c++"扫码进群 本群主要为大家解决工作中遇到的问题遇到的问题发到群里大家集思广益平时可以瞎扯不定期红包
- Mysql常用sql语句(14)- 多表查询
测试必备的Mysql常用sql语句,每天敲一篇,每次敲三遍,每月一循环,全都可记住!! https://www.cnblogs.com/poloyy/category/1683347.html 前言 ...
- 设计模式GOF23之单例模式
单例模式的五种方式 主要:懒汉式,饿汉式 其他:双重检测锁(Double Checking模式),静态内部类,枚举模式 选取时机 延时加载,占用内部资源大:静态内部类好于懒汉 不延时加载,占用内部资源 ...
- [csu1603]贪心
题意:有n门考试,对于考试i,不复习它能考si分,复习它的每小时能提高ai分,每过一小时ai会减小di,也就是说,连续复习某门科目每小时提高的分为ai, ai-di, ai-2di...,但每门考试最 ...
- 曾开源OpenStack,如今Rackspace再次启动IPO
导读:Rackspace开源的OpenStack已成为全球仅次于Linux的第二大开源社区,但Rackspace至今仍在苦苦探索路在何方. 近期有国外媒体爆料,美国云计算厂商Rackspace又悄悄准 ...
- 1025 PAT Ranking (25分) 思路分析 +满分代码
题目 Programming Ability Test (PAT) is organized by the College of Computer Science and Technology of ...
- JS 面向对象封装 无限轮播 插件。
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- Pytorch数据集读取
Pytorch中数据集读取 在机器学习中,有很多形式的数据,我们就以最常用的几种来看: 在Pytorch中,他自带了很多数据集,比如MNIST.CIFAR10等,这些自带的数据集获得和读取十分简便: ...
- python之Python Eclipse+PyDec下载和安装教程(超级详细)
Eclipse 是著名的跨平台 IDE 工具,最初 Eclipse 是 IBM 支持开发的免费 Java 开发工具,2001 年 11 月贡献给开源社区,目前它由非盈利软件供应商联盟 Eclipse ...