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

介绍

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

实现

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;
}
相关推荐
严文文-Chris22 分钟前
【设计模式-享元】
android·java·设计模式
微刻时光23 分钟前
Redis集群知识及实战
数据库·redis·笔记·学习·程序人生·缓存
chnyi6_ya1 小时前
一些写leetcode的笔记
笔记·leetcode·c#
丶白泽1 小时前
重修设计模式-设计原则
设计模式·接口隔离原则·依赖倒置原则·开闭原则
【D'accumulation】1 小时前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
青椒大仙KI112 小时前
24/9/19 算法笔记 kaggle BankChurn数据分类
笔记·算法·分类
liangbm32 小时前
数学建模笔记——动态规划
笔记·python·算法·数学建模·动态规划·背包问题·优化问题
GoppViper3 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理
仙魁XAN3 小时前
Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
unity·设计模式·工厂方法模式·抽象工厂模式
Charles Ray3 小时前
C++学习笔记 —— 内存分配 new
c++·笔记·学习