Menu
CoddyTech

模板方法模式

本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 59 课,共 70 课。

模板方法模式是一种行为型模式,它在基类中定义算法的骨架,让子类可以重写特定步骤,而不改变整体结构。基类控制工作流程,派生类则自定义各个部分。

这种模式使用继承和抽象方法。基类包含一个“模板方法”,它会按顺序调用一系列步骤,其中一些步骤是抽象的,必须由子类实现:

public abstract class DataProcessor
{
    // 模板方法:定义算法结构
    public void Process()
    {
        ReadData();
        ProcessData();
        SaveData();
    }
    
    protected abstract void ReadData();
    protected abstract void ProcessData();
    protected virtual void SaveData() => Console.WriteLine("Saving to default location");
}

public class CsvProcessor : DataProcessor
{
    protected override void ReadData() => Console.WriteLine("Reading CSV file");
    protected override void ProcessData() => Console.WriteLine("Parsing CSV data");
}

public class JsonProcessor : DataProcessor
{
    protected override void ReadData() => Console.WriteLine("Reading JSON file");
    protected override void ProcessData() => Console.WriteLine("Parsing JSON data");
    protected override void SaveData() => Console.WriteLine("Saving to cloud");
}

每个子类都提供自己的实现,而基类负责强制执行操作顺序:

DataProcessor csv = new CsvProcessor();
csv.Process();
// 读取 CSV 文件
// 解析 CSV 数据
// 保存到默认位置

DataProcessor json = new JsonProcessor();
json.Process();
// 读取 JSON 文件
// 解析 JSON 数据
// 保存到云端

当多个类共享相同的算法结构,但在具体步骤上有所不同时,模板方法模式就很适用。它将通用逻辑放在基类中,从而促进代码复用,同时通过可重写的方法保留灵活性。

challenge icon

挑战

简单

让我们使用模板方法模式构建一个报告生成系统。不同类型的报告(例如销售报告和库存报告)有相同的整体结构:它们都需要收集数据、设置格式并输出结果,但每种报告类型处理这些步骤的方式各不相同。

你需要将代码分成三个文件:

  • ReportGenerator.cs:在 Reporting 命名空间中创建一个抽象的 ReportGenerator 类。这个基类通过名为 Generate() 的模板方法定义报告生成算法的框架。该方法应按顺序调用三个步骤:GatherData()、FormatReport() 和 OutputReport()。将 GatherData() 和 FormatReport() 设为抽象方法(每种报告类型都会以不同方式实现它们),而 OutputReport() 应是一个带默认实现的虚方法,该实现会打印 Sending to default printer。
  • ConcreteReports.cs:在同一命名空间中创建两个继承自 ReportGenerator 的具体报告类:
    • SalesReport:实现 GatherData() 并打印 Collecting sales data from database,实现 FormatReport() 并打印 Formatting as sales summary
    • InventoryReport:实现 GatherData() 并打印 Scanning warehouse inventory,实现 FormatReport() 并打印 Formatting as inventory list,并重写 OutputReport() 以打印 Sending to warehouse manager
  • Program.cs:创建报告实例并生成报告,将所有内容整合起来。模板方法确保每份报告都遵循相同的三步流程,但每个步骤的具体行为会根据报告类型而变化。

你将收到一个输入:

  • 报告类型:sales 或 inventory

根据输入创建相应的报告生成器,并调用它的 Generate() 方法。

例如,如果输入是 sales,输出应为:

Collecting sales data from database
Formatting as sales summary
Sending to default printer

如果输入是 inventory,输出应为:

Scanning warehouse inventory
Formatting as inventory list
Sending to warehouse manager

注意,两种报告都遵循完全相同的算法结构(收集、设置格式、输出),但每个步骤的实现都经过了定制。SalesReport 使用默认的输出行为,而 InventoryReport 则重写了该行为。这展示了模板方法模式如何让你控制哪些步骤必须重写,哪些步骤可以选择性地自定义!

亲手试试

using System;
using Reporting;

class Program
{
    public static void Main(string[] args)
    {
        string reportType = Console.ReadLine();
        
        // TODO:根据 reportType 创建相应的报告生成器
        // 如果 reportType 是 "sales",就创建一个 SalesReport
        // 如果 reportType 是 "inventory",就创建一个 InventoryReport
        // 然后调用报告的 Generate() 方法
        
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C# 在线编译器