模板方法模式(Template Method Pattern)

模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

模板方法模式的主要特点包括:

1. 算法骨架

模板方法模式在父类中定义算法的骨架,并在子类中实现具体的步骤。这样,算法的整体结构是固定的,但具体的实现可以由子类决定。

2. 复用父类代码

通过在父类中实现算法的通用部分,可以避免代码重复,子类只需要实现特定的步骤,从而实现代码复用。

3. 扩展灵活性

子类可以通过实现或重写特定的步骤来扩展算法,而不需要修改父类的代码,从而符合开闭原则(对扩展开放,对修改关闭)。

示例

假设我们有一个制作饮料的过程,制作饮料的步骤包括煮水、泡饮料、倒入杯子和添加调料。不同的饮料可能在泡饮料和添加调料的步骤上有所不同。我们可以使用模板方法模式来实现这一过程。

#include <iostream>

// 制作饮料的基类
class Beverage {
public:
    // 模板方法,定义了制作饮料的算法骨架
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    virtual ~Beverage() {}

protected:
    // 固定的步骤
    void boilWater() {
        std::cout << "Boiling water" << std::endl;
    }

    void pourInCup() {
        std::cout << "Pouring into cup" << std::endl;
    }

    // 需要子类实现的步骤
    virtual void brew() = 0;
    virtual void addCondiments() = 0;
};

// 具体子类:茶
class Tea : public Beverage {
protected:
    void brew() override {
        std::cout << "Steeping the tea" << std::endl;
    }

    void addCondiments() override {
        std::cout << "Adding lemon" << std::endl;
    }
};

// 具体子类:咖啡
class Coffee : public Beverage {
protected:
    void brew() override {
        std::cout << "Dripping coffee through filter" << std::endl;
    }

    void addCondiments() override {
        std::cout << "Adding sugar and milk" << std::endl;
    }
};

// 主函数测试
int main() {
    Tea tea;
    Coffee coffee;

    std::cout << "Making tea..." << std::endl;
    tea.prepareRecipe();

    std::cout << "\nMaking coffee..." << std::endl;
    coffee.prepareRecipe();

    return 0;
}

解释

  1. Beverage:这是一个抽象基类,它定义了制作饮料的模板方法prepareRecipe,以及一些固定的步骤(boilWaterpourInCup)。此外,它还定义了两个纯虚函数(brewaddCondiments),这些函数需要在子类中实现。

  2. TeaCoffee:这两个类是具体的子类,它们实现了父类中定义的抽象方法。Tea类实现了泡茶和加柠檬的方法,Coffee类实现了滴滤咖啡和加糖加奶的方法。

  3. 主函数

    • 创建TeaCoffee的实例。
    • 分别调用prepareRecipe方法,这将触发整个制作饮料的过程。

总结

模板方法模式通过在父类中定义算法的骨架,并将具体的实现步骤延迟到子类中,实现了代码的复用和算法的扩展。它适用于有固定算法结构,但其中某些步骤需要由子类具体实现的情况。模板方法模式使得算法的整体结构保持不变,而具体的实现细节可以灵活变化。

补充

在模板方法模式中加入钩子(Hook)可以让子类在不完全覆盖父类方法的情况下,插入特定的行为。钩子通常是一个在基类中定义的方法,它的默认实现是空的或返回一个默认值。子类可以根据需要选择性地覆盖这个钩子方法,以实现特定的行为。

示例:在饮料制作过程中加入钩子

在前面的示例中,我们可以为饮料制作过程加入一个钩子,以便在特定情况下让子类选择是否添加调料。

#include <iostream>

// 制作饮料的基类
class Beverage {
public:
    // 模板方法,定义了制作饮料的算法骨架
    void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) { // 调用钩子方法
            addCondiments();
        }
    }

    virtual ~Beverage() {}

protected:
    // 固定的步骤
    void boilWater() {
        std::cout << "Boiling water" << std::endl;
    }

    void pourInCup() {
        std::cout << "Pouring into cup" << std::endl;
    }

    // 需要子类实现的步骤
    virtual void brew() = 0;
    virtual void addCondiments() = 0;

    // 钩子方法,子类可以覆盖这个方法以添加特定行为
    virtual bool customerWantsCondiments() {
        return true; // 默认情况下添加调料
    }
};

// 具体子类:茶
class Tea : public Beverage {
protected:
    void brew() override {
        std::cout << "Steeping the tea" << std::endl;
    }

    void addCondiments() override {
        std::cout << "Adding lemon" << std::endl;
    }

    // 覆盖钩子方法,提供自己的实现
    bool customerWantsCondiments() override {
        return getUserInput();
    }

private:
    bool getUserInput() {
        char answer;
        std::cout << "Would you like lemon with your tea (y/n)? ";
        std::cin >> answer;
        return (answer == 'y' || answer == 'Y');
    }
};

// 具体子类:咖啡
class Coffee : public Beverage {
protected:
    void brew() override {
        std::cout << "Dripping coffee through filter" << std::endl;
    }

    void addCondiments() override {
        std::cout << "Adding sugar and milk" << std::endl;
    }

    // 覆盖钩子方法,提供自己的实现
    bool customerWantsCondiments() override {
        return getUserInput();
    }

private:
    bool getUserInput() {
        char answer;
        std::cout << "Would you like sugar and milk with your coffee (y/n)? ";
        std::cin >> answer;
        return (answer == 'y' || answer == 'Y');
    }
};

// 主函数测试
int main() {
    Tea tea;
    Coffee coffee;

    std::cout << "Making tea..." << std::endl;
    tea.prepareRecipe();

    std::cout << "\nMaking coffee..." << std::endl;
    coffee.prepareRecipe();

    return 0;
}

解释

  1. Beverage:在基类中,我们增加了一个钩子方法customerWantsCondiments,默认实现是返回true。在prepareRecipe模板方法中,我们在添加调料的步骤前调用了这个钩子方法,根据返回值决定是否添加调料。

  2. TeaCoffee:在这两个子类中,我们覆盖了钩子方法customerWantsCondiments,并实现了自己的用户交互逻辑,询问用户是否需要添加调料。

  3. 主函数

    • 创建TeaCoffee的实例。
    • 调用prepareRecipe方法制作饮料,并根据用户输入决定是否添加调料。

总结

通过在模板方法模式中加入钩子,可以在不改变算法结构的情况下,让子类有机会在特定的步骤上添加自定义行为。钩子提供了一种灵活的机制,使得子类可以选择性地扩展或修改父类的算法,而不需要完全覆盖父类的方法。这增强了模板方法模式的灵活性和可扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值