1 模板方法模式的基本概念
C++ 模板方法模式是一种行为设计模式,它在一个操作中定义算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些特定步骤。在C++中,模板方法模式通常通过使用虚函数和继承来实现。
模板方法模式包括两个主要部分:
(1)抽象类(Abstract Class): 定义了一个或多个操作,其中一些是具体的(由基类实现),而另一些是抽象的(在子类中实现)。这个抽象类还定义了一个模板方法,该方法调用这些操作。模板方法通常被声明为非虚函数,并在基类中实现,以确保其调用顺序不会被子类改变。
(2)具体子类(Concrete Subclass): 继承自抽象类,并实现其中的抽象操作。子类通过提供这些操作的具体实现来扩展算法。
2 模板方法模式的实现步骤
模板方法模式的实现步骤如下:
(1)定义抽象类: 首先,需要定义一个抽象类(或者接口),这个类包含了一个或多个抽象方法(纯虚函数),这些方法在抽象类中没有被实现,需要在子类中实现。同时,这个抽象类还包含一个模板方法,这个方法在抽象类中已经被实现,它调用了那些抽象方法。
(2)实现子类: 然后,需要创建继承自抽象类的子类,并实现那些抽象方法。子类可以重写模板方法,但通常模板方法已经在抽象类中得到了完整的实现,所以子类只需要关注那些抽象方法的实现即可。
(3)使用模板方法: 最后,可以创建子类的实例,并调用模板方法。模板方法会按照其在抽象类中定义的顺序调用那些抽象方法,而这些抽象方法的具体实现则取决于创建的是哪个子类的实例。
如下为样例代码:
cpp
#include <iostream>
#include <string>
#include <memory>
class AbstractClass
{
public:
virtual ~AbstractClass() {}
// 模板方法
void templateMethod() {
specificMethod1();
specificMethod2();
}
// 抽象方法,需要在子类中实现
virtual void specificMethod1() = 0;
virtual void specificMethod2() = 0;
};
class ConcreteClass : public AbstractClass
{
public:
// 实现抽象方法
void specificMethod1() override {
std::cout << "ConcreteClass::specificMethod1() " << std::endl;
}
void specificMethod2() override {
std::cout << "ConcreteClass::specificMethod2() " << std::endl;
}
};
int main()
{
std::shared_ptr<ConcreteClass> concreteClass = std::make_shared<ConcreteClass>();
concreteClass->templateMethod(); // 调用模板方法
return 0;
}
上面代码的输出为:
ConcreteClass::specificMethod1()
ConcreteClass::specificMethod2()
上面代码就是在 C++ 中实现模板方法模式的基本步骤。通过这种方式,可以在不改变算法结构的情况下,通过改变子类的实现来改变算法的行为。
3 模板方法模式的应用场景
C++模板方法模式的应用场景通常出现在那些需要定义一个操作中的算法的骨架,而将一些步骤延迟到子类中实现的情况。具体来说,当多个类有相同的方法,并且逻辑相同,只是细节上有差异时,可以考虑使用模板方法模式。以下是一些具体的应用场景:
(1)统一操作步骤但细节不同: 当多个应用场景具有相同的操作步骤或过程,但具体的操作细节却各不相同时,可以使用模板方法模式。例如,在不同的业务系统中,可能存在类似的业务流程,但某些具体步骤或处理方式却有所不同。此时,可以在抽象类中定义业务流程的骨架,而将具体的处理步骤留给子类去实现。
(2)重构现有代码: 当在重构代码时,如果发现有大量的重复代码,可以将这些重复的代码抽取到父类中,通过模板方法模式来约束其行为。这样不仅可以减少代码的重复,还可以提高代码的可维护性和可读性。
(3)扩展性要求较高的场景: 如果系统需要支持多种不同的业务逻辑,并且这些业务逻辑之间有一定的相似性,那么可以使用模板方法模式。通过定义抽象类和子类,可以方便地添加新的业务逻辑,而无需修改已有的代码。
在使用模板方法模式时,需要注意的是,由于每一个不同的实现都需要一个子类来实现,这可能导致类的个数增加,使得系统更加庞大。因此,在设计时需要权衡利弊,根据具体的需求和场景来选择合适的设计模式。
总的来说,C++ 模板方法模式在统一操作步骤但细节不同的场景、重构现有代码以及扩展性要求较高的场景中都有广泛的应用。
3.1 模板方法模式应用于统一操作步骤但细节不同的场景
假设有一个在线支付系统,其中有多种支付方式(如信用卡支付、支付宝支付等),每种支付方式都有相同的支付流程(如验证支付信息、执行支付操作、记录支付日志等),但具体的验证和支付细节是不同的。
首先,定义一个抽象类 PaymentProcessor,它包含模板方法 processPayment 以及抽象方法 validatePaymentInfo 和 executePayment:
cpp
#include <iostream>
#include <string>
#include <memory>
class PaymentProcessor
{
public:
virtual ~PaymentProcessor() = default;
// 模板方法,定义支付流程的骨架
void processPayment(const std::string& paymentInfo) {
if (validatePaymentInfo(paymentInfo)) {
if (executePayment()) {
recordPaymentLog(paymentInfo);
std::cout << "Payment processed successfully." << std::endl;
}
else {
std::cout << "Payment execution failed." << std::endl;
}
}
else {
std::cout << "Invalid payment information." << std::endl;
}
}
// 抽象方法,需要在子类中实现
virtual bool validatePaymentInfo(const std::string& paymentInfo) = 0;
virtual bool executePayment() = 0;
// 通用方法,记录支付日志(可以是抽象方法,这里直接实现作为示例)
void recordPaymentLog(const std::string& paymentInfo) {
std::cout << "Payment log recorded for info: " << paymentInfo << std::endl;
}
};
接下来,创建两个继承自 PaymentProcessor 的子类 CreditCardProcessor 和 AlipayProcessor,它们分别实现了信用卡支付和支付宝支付的细节:
cpp
class CreditCardProcessor : public PaymentProcessor {
public:
bool validatePaymentInfo(const std::string& paymentInfo) override {
// 信用卡支付信息的验证逻辑
std::cout << "Validating credit card payment info..." << std::endl;
// 假设验证总是成功
return true;
}
bool executePayment() override {
// 执行信用卡支付操作逻辑
std::cout << "Executing credit card payment..." << std::endl;
// 假设支付总是成功
return true;
}
};
class AlipayProcessor : public PaymentProcessor
{
public:
bool validatePaymentInfo(const std::string& paymentInfo) override {
// 支付宝支付信息的验证逻辑
std::cout << "Validating Alipay payment info..." << std::endl;
// 假设验证总是成功
return true;
}
bool executePayment() override {
// 执行支付宝支付操作逻辑
std::cout << "Executing Alipay payment..." << std::endl;
// 假设支付总是成功
return true;
}
};
最后,在 main 函数中,使用智能指针来管理这些支付处理器的生命周期,并调用模板方法 processPayment 来执行支付操作:
cpp
int main()
{
// 使用智能指针管理支付处理器的生命周期
std::unique_ptr<PaymentProcessor> creditCardProcessor = std::make_unique<CreditCardProcessor>();
std::unique_ptr<PaymentProcessor> alipayProcessor = std::make_unique<AlipayProcessor>();
// 执行信用卡支付
creditCardProcessor->processPayment("Credit card payment info");
// 执行支付宝支付
alipayProcessor->processPayment("Alipay payment info");
// 智能指针在离开作用域时会自动释放对象
return 0;
}
上面这些代码的输出为:
Validating credit card payment info...
Executing credit card payment...
Payment log recorded for info: Credit card payment info
Payment processed successfully.
Validating Alipay payment info...
Executing Alipay payment...
Payment log recorded for info: Alipay payment info
Payment processed successfully.
这个简单的示例定义了一个通用的支付流程(processPayment 方法),它包含了验证支付信息、执行支付操作和记录支付日志等步骤。每种支付方式(信用卡和支付宝)都提供了自己特定的验证和支付逻辑,通过继承 PaymentProcessor 类并实现相应的抽象方法来实现。使用智能指针来管理这些对象的生命周期确保了资源的自动释放。
3.2 模板方法模式应用于重构现有代码
假设有一个处理用户订单的系统,它包含多个步骤,如验证订单、处理支付、更新库存和发送确认邮件。这些步骤中的大部分对于不同类型的订单是相似的,但有一些步骤的具体实现是不同的。这种场景使用使用模板方法模式来重构这个系统,使得它更加灵活和易于维护。
首先,定义一个抽象基类 OrderProcessor,它包含了模板方法 processOrder 和几个抽象方法,每个抽象方法对应一个步骤:
cpp
#include <iostream>
#include <memory>
#include <string>
class OrderProcessor
{
public:
virtual ~OrderProcessor() = default;
// 模板方法,定义处理订单的通用流程
void processOrder(const std::string& orderInfo) {
validateOrder(orderInfo);
processPayment(orderInfo);
updateInventory(orderInfo);
sendConfirmationEmail(orderInfo);
}
// 抽象方法,由子类实现具体的验证逻辑
virtual void validateOrder(const std::string& orderInfo) = 0;
// 抽象方法,由子类实现具体的支付处理逻辑
virtual void processPayment(const std::string& orderInfo) = 0;
// 抽象方法,由子类实现具体的库存更新逻辑
virtual void updateInventory(const std::string& orderInfo) = 0;
// 抽象方法,由子类实现具体的发送确认邮件逻辑
virtual void sendConfirmationEmail(const std::string& orderInfo) = 0;
};
接下来,创建具体的订单处理器类,它们继承自 OrderProcessor 并实现具体的步骤:
cpp
class StandardOrderProcessor : public OrderProcessor {
public:
void validateOrder(const std::string& orderInfo) override {
std::cout << "Validating standard order: " << orderInfo << std::endl;
// 实现具体的验证逻辑
}
void processPayment(const std::string& orderInfo) override {
std::cout << "Processing payment for standard order: " << orderInfo << std::endl;
// 实现具体的支付处理逻辑
}
void updateInventory(const std::string& orderInfo) override {
std::cout << "Updating inventory for standard order: " << orderInfo << std::endl;
// 实现具体的库存更新逻辑
}
void sendConfirmationEmail(const std::string& orderInfo) override {
std::cout << "Sending confirmation email for standard order: " << orderInfo << std::endl;
// 实现具体的发送确认邮件逻辑
}
};
class CustomOrderProcessor : public OrderProcessor
{
public:
void validateOrder(const std::string& orderInfo) override {
std::cout << "Validating custom order: " << orderInfo << std::endl;
// 实现自定义订单的验证逻辑
}
// 假设自定义订单不需要处理支付
void processPayment(const std::string& orderInfo) override {
std::cout << "Custom order does not require payment processing." << std::endl;
}
void updateInventory(const std::string& orderInfo) override {
std::cout << "Updating inventory for custom order: " << orderInfo << std::endl;
// 实现自定义订单的库存更新逻辑
}
void sendConfirmationEmail(const std::string& orderInfo) override {
std::cout << "Sending special confirmation email for custom order: " << orderInfo << std::endl;
// 实现自定义订单的发送确认邮件逻辑
}
};
最后,在 main 函数中,使用智能指针来管理这些订单处理器的生命周期,并调用模板方法 processOrder 来处理不同类型的订单:
cpp
int main()
{
// 使用智能指针管理订单处理器的生命周期
std::unique_ptr<OrderProcessor> standardOrder = std::make_unique<StandardOrderProcessor>();
std::unique_ptr<OrderProcessor> customOrder = std::make_unique<CustomOrderProcessor>();
// 处理标准订单
standardOrder->processOrder("Standard order details");
// 处理自定义订单
customOrder->processOrder("Custom order details");
// 智能指针在离开作用域时会自动释放对象
return 0;
}
上面这些代码的输出为:
Validating standard order: Standard order details
Processing payment for standard order: Standard order details
Updating inventory for standard order: Standard order details
Sending confirmation email for standard order: Standard order details
Validating custom order: Custom order details
Custom order does not require payment processing.
Updating inventory for custom order: Custom order details
Sending special confirmation email for custom order: Custom order details
在上述重构示例中,定义了一个通用的订单处理流程,并通过模板方法模式将具体步骤的实现细节留给了子类。这允许为不同类型的订单创建不同的处理器,而无需改变整个处理流程的结构。
现在可以继续完善这个系统,添加更多的订单处理器类来处理其他特殊类型的订单,例如批量订单或预售订单。这些新的订单处理器类将继承自 OrderProcessor 基类,并实现自己的验证、支付、库存更新和发送邮件的逻辑。
此外,还可以考虑添加错误处理逻辑来增强系统的健壮性。例如,如果在某个步骤中发生错误,可以抛出一个异常,并在调用 processOrder 的地方捕获这个异常,然后采取适当的措施,如记录错误日志或通知用户。
4 模板方法模式的优点与缺点
C++ 模板方法模式的优点主要包括:
(1)代码复用: 模板方法模式允许在父类中定义算法的框架,而将一些步骤的实现延迟到子类中。这样,子类可以复用父类中定义的算法结构,只需要实现特定的步骤即可。这大大减少了重复代码,提高了代码复用性。
(2)灵活性: 通过继承并覆写父类中的特定方法,子类可以定制算法中的某些步骤,从而在不改变算法整体结构的情况下实现不同的行为。这使得算法更加灵活,能够适应多种不同的场景。
(3)扩展性: 当需要添加新的算法步骤或修改现有步骤时,只需要在父类或子类中进行相应的修改,而不需要改动使用算法的代码。这降低了维护成本,提高了系统的可扩展性。
(4)封装性: 模板方法模式将算法的实现细节封装在父类和子类中,客户端代码只需要调用模板方法即可,无需关心算法的具体实现。这提高了代码的封装性,降低了客户端代码与算法实现之间的耦合度。
然而,C++ 模板方法模式也存在一些缺点:
(1)继承的局限性: 模板方法模式依赖于继承机制来实现算法的复用和扩展。然而,继承可能会导致类层次结构过于复杂,增加代码的维护难度。此外,如果一个类已经继承了其他类,那么它可能无法再继承模板方法模式的父类,从而限制了该模式的应用范围。
(2)可能违反开闭原则: 虽然模板方法模式在一定程度上支持开闭原则(对扩展开放,对修改封闭),但在某些情况下,当需要添加新的算法步骤时,可能需要对父类或子类进行修改,从而违反了开闭原则。
(3)难以处理不同算法的差异: 当不同的算法在结构上有很大差异时,使用模板方法模式可能会变得困难。因为模板方法模式要求所有算法都遵循相同的结构,这可能会限制算法的灵活性。
(4)性能考虑: 由于模板方法模式通常涉及到虚函数的调用,这可能会引入一定的性能开销。在性能要求较高的场景中,需要谨慎考虑是否使用模板方法模式。