现实中的模板方法
有人说上面这个DEMO太不符合实际。实际上在现实中中模板方法有很多应用,相信大家都有使用模板方法的经历。特别是针对哪些框架程序员,他们既不能规定死框架,又不能什么都不规定。比如.net中的WinForm:窗体的初始化、显示、消息循环都有固定的过程,但是不同的窗体上面有不同的控件,控件有自己具体的参数设置。.net框架的实现者们就规定好这些框架:一个WinForm窗体如何初始化,如何显示。我们这些.net框架的使用者(二手程序员)就可以通过覆盖掉其中的一些步骤来丰富自己的窗体了。有用C/C++写过调用Win32API作窗体的一定知道,一个啥也没有的空窗体就要百几十行的代码,实际上我们发现其中很多代码是重复的,整个代码流程也很类似,我觉得写框架程序的过程就是一个模板方法提取的过程。
下面举一个前几天在群里讨论的一个问题:
private void Page_Load(object sender, System.EventArgs e) { //取出资源文件 rm = (ResourceManager)Application["RM"]; // 在此处放置用户代码以初始化页面 int iLen = MyUICulture.SelectedItem.Text.IndexOf(" ") >= 0 ? MyUICulture.SelectedItem.Text.IndexOf(" ") : MyUICulture.SelectedItem.Text.Length; String SelectedCulture = MyUICulture.SelectedItem.Text.Substring(0, (8 <= iLen ? 8 : iLen)); if (!SelectedCulture.StartsWith("Choose")) { // If another culture was selected, use that instead. Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(SelectedCulture); Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; } this.labCol.Text = rm.GetString("article1"); }
要实现一个国际化的页面,该页面上有一个DropDownList控件MyUICulture,用户可以用该控件选择页面使用的语言。选择后页面上所有控件的Text属性将使用资源文件中的语言字符串进行初始化,并将线程语言特性设为选择的语言。但是碰到一个问题:这个设置语言的过程所有页面流程是一样的,但是不同的页面里面的控件不一样,需要分别初始化,不想每个页面拷贝想同的代码,如何解决这个问题?聪明,就是模板方法模式。我们将这个选择语言的方法作为一个模板方法,而不同的页面里面的初始化控件Text属性放在另外一个虚方法中。将这两个方法放到一个BasePage的父类中,然后所有的页面继承这个BasePage,各个页面可以重新定义这个虚方法。来,我们来实现我们的想法:
public class BasePage : Page …………………. private void Page_Load(object sender, System.EventArgs e) { //调用选择语言方法 SelectLanguage(); } //这是一个虚方法,子类可以覆盖该方法完成自己页面上控件Text属性的初始化 private virtual void SetControlText() { } //选择语言方法,该方法为一个模板方法,调用SetControlText完成自己的算法。 private void SelectLanguage() { //取出资源文件 rm = (ResourceManager)Application["RM"]; // 在此处放置用户代码以初始化页面 int iLen = MyUICulture.SelectedItem.Text.IndexOf(" ") >= 0 ? MyUICulture.SelectedItem.Text.IndexOf(" ") : MyUICulture.SelectedItem.Text.Length; String SelectedCulture = MyUICulture.SelectedItem.Text.Substring(0, (8 <= iLen ? 8 : iLen)); if (!SelectedCulture.StartsWith("Choose")) { // If another culture was selected, use that instead. Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(SelectedCulture); Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; } //调用设置自己页面控件的Text属性的方法 SetControlText(); }
下面是某个实现了BasePage的页面:
public class MyPage : BasePage ....... public override void SetControlText() { this.labCol.Text =rm.GetString("article1"); }
模板方法和其他模式的联系
各个模式之间都有联系,模板方法也不例外,她并不是孤立存在的。模板中的那些虚方法实际上都是使用工厂方法设计模式,将父类的执行逻辑延迟到子类。有的时候模板方法里定义算法的步骤会用到策略模式,因为有的时候这个算法不止一种,比如上面的教育部规定新生报到流程这个算法,有可能教育部规定了三四种,那么我们就可以用策略模式封装这几套算法。
感想
学习模式好久了,总是一种模模糊糊的感觉,现在只体会到一点:在开始一个设计之前不要就想着使用那个那个模式,不要一个个的往上套,模式是用来解决问题的,不是用来设计的,在编码阶段,如果你碰到难以解决的问题,比如耦合度太高啊,很难添加新特性了,那么你就应该回过头来重构你的程序,这个时候模式就为你指明了重构的方向。如果你的设计没有出现问题,你的编码没有出现任何困难,你的程序很容易添加新特性也很好维护,那么就不要引入模式了,并不是你的程序用到了模式你的程序就是好的。