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

介绍

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

实现

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;
}
相关推荐
是2的10次方啊3 小时前
设计模式与生活⑩:地铁没变,你换了个状态
设计模式
乐橙开放平台3 小时前
离岗检测怎么实现?通过客流相机或 AI 摄像头识别岗位区域无人状态,再推送告警消息
人工智能·笔记·后端·数码相机·物联网·自动化·音视频
蒸蒸yyyyzwd13 小时前
AI应用相关可能的面试问题
笔记
叼烟扛炮14 小时前
[Git 实战] 学习笔记:从生成 SSH 密钥到解决 TortoiseGit 推送报错全指南
linux·运维·笔记·git·学习
蒸蒸yyyyzwd15 小时前
学习笔记 八股手撕
笔记·学习
揣一尾游鱼yu16 小时前
Spring上(IOC)笔记
java·笔记·spring·ioc
小李不想当小白18 小时前
BKP备份寄存器与RTC实时时钟(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
瑞码空间19 小时前
JSP与Servlet核心开发与CRUD实战笔记
java·笔记·servlet·jsp
La Pulga20 小时前
【FreeRTOS源码学习笔记】链表的实现
数据结构·笔记·stm32·mcu·链表·嵌入式·freertos
步行cgn21 小时前
适配器设计模式:一个“翻译官”的故事
设计模式