模板方法模式通过在基类中定义一个模板方法,该方法封装了一个算法的固定步骤,然后允许子类实现或重写这些步骤。这样,子类可以定制算法的具体行为,而无需改变算法的整体结构。
模板方法模式包含以下角色:
UML 类图
+---------------------------+
| AbstractClass |
+---------------------------+
| + TemplateMethod(): void |
| + Step1(): void |
| + Step2(): void |
| - Step3(): void |
+---------------------------+
^
|
+-------------------+
| ConcreteClass |
+-------------------+
| - Step3(): void |
+-------------------+
假设我们要制作一杯饮料,制作的过程包括煮水、冲泡、倒入杯中、添加配料等步骤。咖啡和茶是两种不同的饮料,它们在制作过程中的步骤基本相同,但在某些步骤上有所不同。我们可以使用模板方法模式来实现这个场景。
抽象类
// 抽象类 - 饮料制作过程
public abstract class Beverage
{
// 模板方法
public void PrepareRecipe()
{
BoilWater();
Brew();
PourInCup();
AddCondiments();
}
// 具体方法 - 煮水
private void BoilWater()
{
Console.WriteLine("Boiling water");
}
// 抽象方法 - 冲泡
protected abstract void Brew();
// 具体方法 - 倒入杯中
private void PourInCup()
{
Console.WriteLine("Pouring into cup");
}
// 抽象方法 - 添加配料
protected abstract void AddCondiments();
}
具体类
// 具体类 - 茶
public class Tea : Beverage
{
protected override void Brew()
{
Console.WriteLine("Steeping the tea");
}
protected override void AddCondiments()
{
Console.WriteLine("Adding lemon");
}
}
// 具体类 - 咖啡
public class Coffee : Beverage
{
protected override void Brew()
{
Console.WriteLine("Dripping coffee through filter");
}
protected override void AddCondiments()
{
Console.WriteLine("Adding sugar and milk");
}
}
客户端代码
class Program
{
static void Main(string[] args)
{
Beverage tea = new Tea();
tea.PrepareRecipe(); // 制作茶
Console.WriteLine();
Beverage coffee = new Coffee();
coffee.PrepareRecipe(); // 制作咖啡
}
}
运行结果
Boiling water
Steeping the tea
Pouring into cup
Adding lemon
Boiling water
Dripping coffee through filter
Pouring into cup
Adding sugar and milk
在这个例子中,Beverage
是抽象类,定义了一个模板方法 PrepareRecipe()
,它包含了制作饮料的固定步骤。这些步骤中,有些是具体实现的(如 BoilWater()
和 PourInCup()
),而有些是抽象方法,由子类 Tea
和 Coffee
来实现(如 Brew()
和 AddCondiments()
)。客户端代码可以使用不同的具体类来制作不同的饮料,而不需要关心具体的实现细节。
优点:
代码复用: 将通用的算法步骤封装到基类中,子类只需要实现差异化的部分,减少了重复代码。
扩展性强: 子类可以根据需要重写或扩展某些步骤,增加算法的灵活性。
控制算法结构: 父类定义了算法的骨架,确保了算法的整体结构不被子类破坏。
缺点:
增加代码复杂性: 由于引入了继承关系和抽象方法,可能会使代码结构变得复杂。
对子类的依赖: 父类依赖子类来实现某些步骤,可能导致子类必须实现某些方法,即使这些方法在特定情况下并不需要。
模板方法模式通过将通用的算法步骤封装到抽象类中,允许子类重写或扩展特定的步骤,实现了算法的复用和扩展。它确保了算法的整体结构不被破坏,同时为子类提供了灵活性。在多个类具有相似的操作步骤时,模板方法模式是一种非常有效的设计模式。