《设计模式的艺术》笔记 - 模板方法模式

介绍

模板方法模式定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法模式是一种类行为模式。

实现

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Abstract {    // 抽象基类
public:
    void templateMethod();  // 模板方法
    virtual void primitiveOperation1(); // 基本操作1
    virtual void primitiveOperation2(); // 基本操作2
    virtual void primitiveOperation3(); // 基本操作3
};

class Concrete : public Abstract {  // 具体子类,重写基本操作方法
public:
    void primitiveOperation1() override;

    void primitiveOperation2() override;

    void primitiveOperation3() override;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>

void Abstract::primitiveOperation1() {
    std::cout << "默认基本操作1" << std::endl;
}

void Abstract::primitiveOperation2() {
    std::cout << "默认基本操作2" << std::endl;
}

void Abstract::primitiveOperation3() {
    std::cout << "默认基本操作3" << std::endl;
}

void Abstract::templateMethod() {
    primitiveOperation1();
    primitiveOperation2();
    primitiveOperation3();
}

void Concrete::primitiveOperation1() {
    std::cout << "重写基本操作1" << std::endl;
}

void Concrete::primitiveOperation2() {
    std::cout << "重写基本操作2" << std::endl;
}

void Concrete::primitiveOperation3() {
    std::cout << "重写基本操作3" << std::endl;
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    Abstract *abstract = new Concrete();
    abstract->templateMethod();
    delete abstract;

    return 0;
}

总结

优点

  1. 模板方法模式在父类中形式化地定义一个算法,而由它的子类来实现细节的处理。在子类实现详细的处理算法时并不会改变算法中步骤的执行次序。

  2. 模板方法模式是一种代码复用技术,它在类库设计中尤为重要。它提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为。它鼓励恰当使用继承来实现代码复用。

  3. 模板方法模式可实现一种反向控制结构。通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。

  4. 在模板方法模式中可以通过子类来覆盖父类的基本方法,不同的子类可以提供基本方法的不同实现,更换和增加新的子类很方便,符合单一职责原则和开闭原则。

缺点

  1. 需要为每一个基本方法的不同实现提供一个子类。如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大,设计也更加抽象。此时,可结合桥接模式来进行设计。

适用场景

  1. 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以改变的细节由其子类来实现。即一次性地实现一个算法的不变部分,并将可变的行为留给子类来实现。

  2. 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。

  3. 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

练习

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class DatabaseOperator {    // 抽象基类
public:
    void operation(const std::string &sql, bool update = false);  // 模板方法
    virtual void connect() = 0; // 连接数据库
    virtual void open() = 0; // 打开数据库
    virtual void query(const std::string &sql) = 0; // 查询数据库
    virtual void update(const std::string &sql) = 0; // 更新数据库
    virtual void close() = 0; // 关闭数据库
};

class JDBCBridgeODBC : public DatabaseOperator {  // 具体子类,重写基本操作方法
public:
    void connect() override;

    void open() override;

    void query(const std::string &sql) override;

    void update(const std::string &sql) override;

    void close() override;
};

class ManufacturerDriver : public DatabaseOperator { // 具体子类,重写基本操作方法
public:
    void connect() override;

    void open() override;

    void query(const std::string &sql) override;

    void update(const std::string &sql) override;

    void close() override;
};

class ThreadPool : public DatabaseOperator {    // 具体子类,重写基本操作方法
public:
    void connect() override;

    void open() override;

    void query(const std::string &sql) override;

    void update(const std::string &sql) override;

    void close() override;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>

void DatabaseOperator::operation(const std::string &sql, bool update) {
    connect();
    open();
    if (update) {
        this->update(sql);
    } else {
        query(sql);
    }
    this->close();
}

void JDBCBridgeODBC::connect() {
    std::cout << "JDBC-ODBC桥接方式连接数据库" << std::endl;
}

void JDBCBridgeODBC::open() {
    std::cout << "JDBC-ODBC桥接方式打开数据库" << std::endl;
}

void JDBCBridgeODBC::query(const std::string &sql) {
    std::cout << "JDBC-ODBC桥接方式查询数据库" << sql << std::endl;
}

void JDBCBridgeODBC::update(const std::string &sql) {
    std::cout << "JDBC-ODBC桥接方式更新数据库" << sql << std::endl;
}

void JDBCBridgeODBC::close() {
    std::cout << "JDBC-ODBC桥接方式关闭数据库" << std::endl;
}

void ManufacturerDriver::connect() {
    std::cout << "厂商驱动方式连接数据库" << std::endl;
}

void ManufacturerDriver::open() {
    std::cout << "厂商驱动方式打开数据库" << std::endl;
}

void ManufacturerDriver::query(const std::string &sql) {
    std::cout << "厂商驱动方式查询数据库" << sql << std::endl;
}

void ManufacturerDriver::update(const std::string &sql) {
    std::cout << "厂商驱动方式更新数据库" << sql << std::endl;
}

void ManufacturerDriver::close() {
    std::cout << "厂商驱动方式关闭数据库" << std::endl;
}

void ThreadPool::connect() {
    std::cout << "线程池方式连接数据库" << std::endl;
}

void ThreadPool::open() {
    std::cout << "线程池方式打开数据库" << std::endl;
}

void ThreadPool::query(const std::string &sql) {
    std::cout << "线程池方式查询数据库" << sql << std::endl;
}

void ThreadPool::update(const std::string &sql) {
    std::cout << "线程池方式更新数据库" << sql << std::endl;
}

void ThreadPool::close() {
    std::cout << "线程池方式关闭数据库" << std::endl;
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    DatabaseOperator *jdbc = new JDBCBridgeODBC();
    jdbc->operation("select * from t", false);
    jdbc->operation("drop table t", false);
    delete jdbc;

    return 0;
}
相关推荐
亭台4 小时前
【Matlab笔记_23】MATLAB的工具包m_map的m_image和m_pcolor区别
笔记·算法·matlab
alibli4 小时前
一文学会设计模式之结构型模式及最佳实现
c++·设计模式
Jack___Xue5 小时前
LangChain实战快速入门笔记(五)--LangChain使用之Tools
笔记·microsoft·langchain
走在路上的菜鸟6 小时前
Android学Dart学习笔记第十三节 注解
android·笔记·学习·flutter
hhy_smile6 小时前
Android 与 java 设计笔记
android·java·笔记
YJlio7 小时前
BgInfo 学习笔记(11.5):多种输出方式(壁纸 / 剪贴板 / 文件)与“更新其他桌面”实战
笔记·学习·c#
断剑zou天涯7 小时前
【算法笔记】线段树SegmentTree
数据结构·笔记·算法
电子科技圈8 小时前
SiFive车规级RISC-V IP获IAR最新版嵌入式开发工具全面支持,加速汽车电子创新
嵌入式硬件·tcp/ip·设计模式·汽车·代码规范·risc-v·代码复审
自不量力的A同学8 小时前
ionet 25.2 发布
笔记
YJlio8 小时前
桌面工具学习笔记(11.4):BgInfo + Desktops + ZoomIt 组合拳——演示与排障环境一键到位
笔记·学习·自动化