设计模式:模版方法(Template Method)
作者:TerryLee 创建于:2006-07-04 出处:http://terrylee.cnblogs.com/archive/2006/07/04/DesignPattern_TemplateMethod.html 收录于:2013-02-28
结构图
意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用性
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是O p d y k e 和J o h n s o n 所描述过的“重分解以一般化”的一个很好的例子[ O J 9 3 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
- 控制子类扩展。模板方法只在特定点调用“h o o k ”操作(参见效果一节),这样就只允许在这些点进行扩展。
实现代码
假如我们需要简单的读取Northwind数据库中的表的记录并显示出来。对于数据库操作,我们知道不管读取的是哪张表,它一般都应该经过如下这样的几步:
1.连接数据库(Connect)
2.执行查询命令(Select)
3.显示数据(Display)
4.断开数据库连接(Disconnect)
这些步骤是固定的,但是对于每一张具体的数据表所执行的查询却是不一样的。显然这需要一个抽象角色,给出顶级行为的实现。显然在这个顶级的框架DataAccessObject中给出了固定的轮廓,方法Run()便是模版方法,Template Method模式也由此而得名。而对于Select()和Display()这两个抽象方法则留给具体的子类去实现。
1 using Sytem; 2 public abstract class DataAccessObject 3 { 4 protected string connectionString; 5 protected DataSet dataSet; 6 public virtual void Connect() 7 { 8 connectionString ="Server=.;User Id=sa;Password=sa;Database=Northwind"; 9 } 10 public abstract void Select(); 11 public abstract void Display(); 12 public virtual void Disconnect() 13 { 14 connectionString = ""; 15 } 16 // The "Template Method" 17 public void Run() 18 { 19 Connect(); 20 Select(); 21 Display(); 22 Disconnect(); 23 } 24 } 25 class Categories : DataAccessObject 26 { 27 public override void Select() 28 { 29 string sql = "select CategoryName from Categories"; 30 SqlDataAdapter dataAdapter = new SqlDataAdapter( sql, connectionString); 31 dataSet = new DataSet(); 32 dataAdapter.Fill(dataSet, "Categories"); 33 } 34 public override void Display() 35 { 36 Console.WriteLine("Categories ---- "); 37 DataTable dataTable = dataSet.Tables["Categories"]; 38 foreach (DataRow row in dataTable.Rows) 39 { 40 Console.WriteLine(row["CategoryName"].ToString()); 41 } 42 Console.WriteLine(); 43 } 44 } 45 class Products : DataAccessObject 46 { 47 public override void Select() 48 { 49 string sql = "select top 10 ProductName from Products"; 50 SqlDataAdapter dataAdapter = new SqlDataAdapter( sql, connectionString); 51 dataSet = new DataSet(); 52 dataAdapter.Fill(dataSet, "Products"); 53 } 54 public override void Display() 55 { 56 Console.WriteLine("Products ---- "); 57 DataTable dataTable = dataSet.Tables["Products"]; 58 foreach (DataRow row in dataTable.Rows) 59 { 60 Console.WriteLine(row["ProductName"].ToString()); 61 } 62 Console.WriteLine(); 63 } 64 } 65 public class App 66 { 67 static void Main() 68 { 69 DataAccessObject dao; 70 dao = new Categories(); 71 dao.Run(); 72 dao = new Products(); 73 dao.Run(); 74 // Wait for user 75 Console.Read(); 76 } 77 }
在上面的例子中,需要注意的是:
1.对于Connect()和Disconnect()方法实现为了virtual,而Select()和Display()方法则为abstract,这是因为如果这个方法有默认的实现,则实现为virtual,否则为abstract。
2.Run()方法作为一个模版方法,它的一个重要特征是:在基类里定义,而且不能够被派生类更改。有时候它是私有方法(private
method),但实际上它经常被声明为protected。它通过调用其它的基类方法(覆写过的)来工作,但它经常是作为初始化过程的一部分被调用的,这样就没必要让客户端程序员能够直接调用它了。
3.在一开始我们提到了不管读的是哪张数据表,它们都有共同的操作步骤,即共同点。因此可以说Template
Method模式的一个特征就是剥离共同点。
实现要点
1.Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提了灵活的扩展点,是代码复用方面的基本实现结构。
2.除了可以灵活应对子步骤的变化外,“不用调用我,让我来调用你”的反向控制结构是Template
Method的典型应用。
3.在具体实现方面,被Template
Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,纯虚方法),但一般推荐将它们设置为protected方法。
总结
Template Method模式是非常简单的一种设计模式,但它却是代码复用的一项基本的技术,在类库中尤其重要。