旧游无处不堪寻
无寻处,惟有少年心
设计模式-模板方法

模板方法模式(Template Method Pattern)是一种行为型设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。

解释


模板方法模式建议将算法分解为一系列步骤,然后将这些步骤改写为方法,最后在”模板方法”中依次调用这些方法。
步骤可以是抽象的,也可以有一些默认的实现。为了能够使用算法,客户端需要自行提供子类并实现所有的抽象步骤。如有必要还需重写一些步骤。

模板方法模式 UML


适用场景


  1. 当你只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时,可使用模板方法模式
  2. 当多个类的算法除一些细微不同之外几乎完全一样时,你可使用该模式。但其后果就是,只要算法发生变化,你就可能需要修改所有的类

代码示例


using System;
namespace TemplateMethodPatternDemo
{
// The Abstract Class defines a template method that contains a skeleton of
// some algorithm, composed of calls to (usually) abstract primitive
// operations.
//
// Concrete subclasses should implement these operations, but leave the
// template method itself intact.
abstract class AbstractClass
{
// The template method defines the skeleton of an algorithm.
public void TemplateMethod()
{
this.BaseOperation1();
this.RequiredOperations1();
this.BaseOperation2();
this.Hook1();
this.RequiredOperation2();
this.BaseOperation3();
this.Hook2();
}

// These operations already have implementations.
protected void BaseOperation1()
{
Console.WriteLine("AbstractClass says: I am doing the bulk of the work");
}

protected void BaseOperation2()
{
Console.WriteLine("AbstractClass says: But I let subclasses override some operations");
}

protected void BaseOperation3()
{
Console.WriteLine("AbstractClass says: But I am doing the bulk of the work anyway");
}

// These operations have to be implemented in subclasses.
protected abstract void RequiredOperations1();

protected abstract void RequiredOperation2();

// These are "hooks." Subclasses may override them, but it's not
// mandatory since the hooks already have default (but empty)
// implementation. Hooks provide additional extension points in some
// crucial places of the algorithm.
protected virtual void Hook1() { }

protected virtual void Hook2() { }
}

// Concrete classes have to implement all abstract operations of the base
// class. They can also override some operations with a default
// implementation.
class ConcreteClass1 : AbstractClass
{
protected override void RequiredOperations1()
{
Console.WriteLine("ConcreteClass1 says: Implemented Operation1");
}

protected override void RequiredOperation2()
{
Console.WriteLine("ConcreteClass1 says: Implemented Operation2");
}
}

// Usually, concrete classes override only a fraction of base class'
// operations.
class ConcreteClass2 : AbstractClass
{
protected override void RequiredOperations1()
{
Console.WriteLine("ConcreteClass2 says: Implemented Operation1");
}

protected override void RequiredOperation2()
{
Console.WriteLine("ConcreteClass2 says: Implemented Operation2");
}

protected override void Hook1()
{
Console.WriteLine("ConcreteClass2 says: Overridden Hook1");
}
}

class Client
{
// The client code calls the template method to execute the algorithm.
// Client code does not have to know the concrete class of an object it
// works with, as long as it works with objects through the interface of
// their base class.
public static void ClientCode(AbstractClass abstractClass)
{
// ...
abstractClass.TemplateMethod();
// ...
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Same client code can work with different subclasses:");

Client.ClientCode(new ConcreteClass1());

Console.Write("\n");

Console.WriteLine("Same client code can work with different subclasses:");
Client.ClientCode(new ConcreteClass2());
// output

// Same client code can work with different subclasses:
// AbstractClass says: I am doing the bulk of the work
// ConcreteClass1 says: Implemented Operation1
// AbstractClass says: But I let subclasses override some operations
// ConcreteClass1 says: Implemented Operation2
// AbstractClass says: But I am doing the bulk of the work anyway

// Same client code can work with different subclasses:
// AbstractClass says: I am doing the bulk of the work
// ConcreteClass2 says: Implemented Operation1
// AbstractClass says: But I let subclasses override some operations
// ConcreteClass2 says: Overridden Hook1
// ConcreteClass2 says: Implemented Operation2
// AbstractClass says: But I am doing the bulk of the work anyway
}
}
}