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

介绍

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

实现

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;
}
相关推荐
黎宇幻生7 小时前
Java全栈学习笔记39
java·笔记·学习
遇印记10 小时前
大二java学习笔记:二维数组
java·笔记·学习
ShareBeHappy_Qin11 小时前
Spring 中使用的设计模式
java·spring·设计模式
bnsarocket12 小时前
Verilog和FPGA的自学笔记6——计数器(D触发器同步+异步方案)
笔记·fpga开发·verilog·自学·硬件编程
LK_0713 小时前
【Open3D】Ch.3:顶点法向量估计 | Python
开发语言·笔记·python
li星野13 小时前
打工人日报#20251011
笔记·程序人生·fpga开发·学习方法
摇滚侠13 小时前
Spring Boot 3零基础教程,yml配置文件,笔记13
spring boot·redis·笔记
QT 小鲜肉13 小时前
【个人成长笔记】在Ubuntu中的Linux系统安装 anaconda 及其相关终端命令行
linux·笔记·深度学习·学习·ubuntu·学习方法
QT 小鲜肉13 小时前
【个人成长笔记】在Ubuntu中的Linux系统安装实验室WIFI驱动安装(Driver for Linux RTL8188GU)
linux·笔记·学习·ubuntu·学习方法
急急黄豆14 小时前
MADDPG学习笔记
笔记·学习