设计模式4-模版方法

设计模式

  • 重构获得模式
  • 重构的关键技法
      • 1. 静态转动态
      • 2. 早绑定转晚绑定
      • 3. 继承转组合
      • 4. 编译时依赖转运行时依赖
      • 5. 紧耦合转松耦合
  • 组件协助
    • 动机
    • 模式定义
      • 结构
    • 要点总结。
  • 例子
      • 示例解释:

重构获得模式

设计模式的目的是应对变化,提高复用

设计模式的要点是寻找变化点,然后在变化点处应用设计模式。从而更好地应对需求的变化。什么时候什么地点应用设计模式。比设计模式结构本身更为重要。
设计模式的应用,不应当先入为主,一上来就套用设计模式这是设计模式的最大误用,没有一步到位的设计。敏捷软件开发实践提倡的是refacroring to Patterns(重构), 是目前普遍公认的最好使用的设计模式的方法。

推荐书籍

在这里插入图片描述

重构的关键技法

让变化可配置
重构是改善现有代码设计的过程,通过一系列的技术手法和策略,使得代码更易于理解、扩展和维护。以下是几种重构的关键技法,以及它们在代码中的应用示例:

1. 静态转动态

技法说明

  • 将在编译时确定的部分改为在运行时确定,增加代码的灵活性和适应性。

示例

  • 将硬编码的条件判断改为通过配置文件或者用户输入来确定。
// 静态
bool isFeatureEnabled() {
    return true;  // 静态确定特性总是启用
}

// 动态
bool isFeatureEnabled() {
    // 通过配置文件或者用户输入来确定特性是否启用
    return config.isEnabled("featureX");
}

2. 早绑定转晚绑定

技法说明

  • 将函数调用的绑定从编译时确定延迟到运行时,使得对象能够在运行时选择调用不同的方法或策略。

示例

  • 使用虚函数和多态来实现晚绑定,而不是通过静态绑定的方式。
// 早绑定
class Shape {
public:
    void draw() {
        // 绘制代码
    }
};

// 晚绑定
class Shape {
public:
    virtual void draw() {
        // 绘制代码
    }
};

class Circle : public Shape {
public:
    void draw() override {
        // 绘制圆形的代码
    }
};

class Rectangle : public Shape {
public:
    void draw() override {
        // 绘制矩形的代码
    }
};

void render(Shape* shape) {
    shape->draw();  // 晚绑定,根据实际对象类型调用对应的draw方法
}

3. 继承转组合

技法说明

  • 通过组合现有类的实例来代替继承,以减少类之间的耦合性,同时提高代码的灵活性和可维护性。

示例

  • 将继承关系改为对象组合关系。
// 继承
class Car {
public:
    virtual void drive() {
        // 驾驶汽车
    }
};

class SportsCar : public Car {
public:
    void drive() override {
        // 驾驶运动汽车
    }
};

// 组合
class Engine {
public:
    void start() {
        // 启动引擎
    }
};

class Car {
private:
    Engine engine;

public:
    void drive() {
        engine.start();
        // 驾驶汽车
    }
};

class SportsCar {
private:
    Engine engine;

public:
    void drive() {
        engine.start();
        // 驾驶运动汽车
    }
};

4. 编译时依赖转运行时依赖

技法说明

  • 将程序依赖关系从编译时硬编码转为运行时根据条件动态确定,提高程序的灵活性和可配置性

示例

  • 使用依赖注入或者配置来动态确定依赖。
// 编译时依赖
class Database {
public:
    void saveData(const std::string& data) {
        // 保存数据到 MySQL 数据库
    }
};

// 运行时依赖
class Database {
public:
    virtual void saveData(const std::string& data) = 0;
};

class MySQLDatabase : public Database {
public:
    void saveData(const std::string& data) override {
        // 保存数据到 MySQL 数据库
    }
};

class PostgresDatabase : public Database {
public:
    void saveData(const std::string& data) override {
        // 保存数据到 PostgreSQL 数据库
    }
};

void saveData(Database* db, const std::string& data) {
    db->saveData(data);  // 根据运行时传入的数据库对象调用相应的方法
}

5. 紧耦合转松耦合

技法说明

  • 减少模块或者类之间的依赖关系,使得各个模块之间可以独立开发、测试和部署。

示例

  • 使用依赖注入、接口抽象等手段降低模块间的耦合度。
// 紧耦合
class ServiceA {
public:
    void doSomething() {
        ServiceB b;
        b.doSomethingElse();
    }
};

class ServiceB {
public:
    void doSomethingElse() {
        // 实现
    }
};

// 松耦合
class ServiceB {
public:
    void doSomethingElse() {
        // 实现
    }
};

class ServiceA {
private:
    ServiceB& b;

public:
    ServiceA(ServiceB& b) : b(b) {}

    void doSomething() {
        b.doSomethingElse();
    }
};

通过这些关键技法,可以更好地进行代码重构,改进现有设计,使得代码更易于维护、扩展和重用,同时提高系统的灵活性和可靠性。

组件协助

定义:现代软件分专业分工之后的第一个结果是框架与应用的区分组建协作模式,通过完绑定来实现框架与应用间的松耦合是二者之间协作时常用的模式。

典型模式
• Template Method
• Observer / Event
• Strategy

动机

在软件构建过程中,每一项任务它常常有稳定的整体操作结构。但各个子步骤却有很多变化的需求。或者由于固定的原因,比如框架与应用之间的关系。而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚七实现需求?

结构化软件设计流程

在这里插入图片描述

面向对象软件设计流程

在这里插入图片描述

早绑定与晚绑定

在这里插入图片描述

模式定义

定义一个操作中的算法框架(稳定),而将一些步骤延迟(变化)到子类中template Method 使得子类可以不改变。一个算法的结构即可重新定义就是重写该算法的某些特定步骤。

结构

在这里插入图片描述

要点总结。

模板方法模式是一种非常基础性的设计模式。在面向对象系统中有着大量的应用。他用最简洁的机制也就是虚函数的多态性。为很多应用程序架构提供了灵活的扩展点。是代码复用方面的基本实现结构

除了可以灵活应对自步骤的变化以外,不要调用我,让我来调用你。反向控制结构是模板方法的典型应用。

在具体实验方面,被模板方法调用的虚方法可以具有实现,也可以没有任何实现。也就是所谓的抽象方法和纯虚方法。但一般推荐将他们设置为保护方法

例子

Template Method(模板方法)模式是一种行为设计模式,定义了一个操作中的算法的框架,将一些步骤推迟到子类中实现。这种模式是通过创建一个模板方法,该方法在一个算法的结构中定义了算法的骨架,而将一些步骤的实现延迟到子类中。

以下是一个简单的 C++ 示例,演示了模板方法模式的实现:

#include <iostream>

// 抽象基类
class AbstractClass {
public:
    // 模板方法,定义了算法的骨架
    void templateMethod() {
        operation1();
        commonOperation(); // 模板方法中可以包含具体实现
        operation2();
        if (hookMethod()) { // 钩子方法,可选覆盖
            optionalOperation();
        }
    }

protected:
    // 纯虚函数,需要在子类中实现
    virtual void operation1() = 0;
    virtual void operation2() = 0;

    // 具体方法,子类共享的实现
    void commonOperation() {
        std::cout << "AbstractClass: commonOperation" << std::endl;
    }

    // 钩子方法,可选覆盖
    virtual bool hookMethod() {
        return true; // 默认实现
    }

    // 可选的具体方法,子类可以选择性地覆盖
    virtual void optionalOperation() {
        std::cout << "AbstractClass: optionalOperation" << std::endl;
    }
};

// 具体子类实现
class ConcreteClass1 : public AbstractClass {
protected:
    void operation1() override {
        std::cout << "ConcreteClass1: operation1" << std::endl;
    }

    void operation2() override {
        std::cout << "ConcreteClass1: operation2" << std::endl;
    }

    bool hookMethod() override {
        std::cout << "ConcreteClass1: hookMethod" << std::endl;
        return false; // 覆盖钩子方法
    }
};

// 另一个具体子类
class ConcreteClass2 : public AbstractClass {
protected:
    void operation1() override {
        std::cout << "ConcreteClass2: operation1" << std::endl;
    }

    void operation2() override {
        std::cout << "ConcreteClass2: operation2" << std::endl;
    }

    // 这里没有覆盖钩子方法
};

// 客户端代码
int main() {
    AbstractClass* object1 = new ConcreteClass1();
    AbstractClass* object2 = new ConcreteClass2();

    // 调用模板方法,执行算法
    object1->templateMethod();
    std::cout << std::endl;
    object2->templateMethod();

    delete object1;
    delete object2;

    return 0;
}

示例解释:

  1. AbstractClass 是抽象基类,定义了模板方法 templateMethod() 和一些具体方法 commonOperation()optionalOperation(),以及一个钩子方法 hookMethod()

  2. ConcreteClass1ConcreteClass2 是具体子类,分别实现了抽象类中的纯虚函数 operation1()operation2(),并选择性地覆盖了钩子方法 hookMethod()

  3. 在客户端代码中,创建了 ConcreteClass1ConcreteClass2 的对象,并调用它们的 templateMethod() 方法,执行了定义好的算法。

  4. 输出结果展示了每个步骤的执行顺序,包括了钩子方法的选择性覆盖情况。

这个例子展示了模板方法模式的核心思想:定义一个算法的骨架,将特定的步骤延迟到子类中去实现,从而在不同的子类中实现算法的不同部分,同时保持算法结构的稳定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/731694.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据通信与网络(五)

交换机功能&#xff1a; 地址学习&#xff08;端口/MAC地址映射表&#xff09; 通信过滤&#xff08;基于端口/MAC地址映射表&#xff09; 生成树协议&#xff08;断开环路&#xff09; 隔离冲突域 生成树协议 隔离冲突域 交换机配置模式(用不同级别的命令对交换机进行配置) 普…

Zookeeper原理

Zookeeper监听原理 监听原理详解 &#xff08;1&#xff09;首先要有一个main()线程 &#xff08;2&#xff09;在main线程中创建Zookeeper客户端&#xff0c;这时就会创建两个线程&#xff0c;一个负责网络连接通信(connet)&#xff0c;一个负责监听(listener) 。 &#xf…

PHP转Go系列 | ThinkPHP与Gin的使用姿势

大家好&#xff0c;我是码农先森。 安装 使用 composer 进行项目的创建。 composer create-project topthink/think thinkphp_demo使用 go mod 初始化项目。 go mod init gin_demo目录 thinkphp_demo 项目目录结构。 thinkphp_demo ├── LICENSE.txt ├── README.md …

【STM32】时钟树系统

1.时钟树简介 1.1五个时钟源 LSI是低速内部时钟&#xff0c;RC振荡器&#xff0c;频率为32kHz左右。供独立看门狗和自动唤醒单元使用。 LSE是低速外部时钟&#xff0c;接频率为32.768kHz的石英晶体。这个主要是RTC的时钟源。 HSE是高速外部时钟&#xff0c;可接石英*/陶瓷谐振…

Ubuntu 22.04安装 docker

安装过程和指令 # 1.升级 apt sudo apt update # 2.安装docker sudo apt install docker.io docker-compose # 3.将当前用户加入 docker组 sudo usermod -aG docker ${USER} # 4. 重启 # 5. 查看镜像 docker ps -a 或者 docker images # 6. 下载镜像 docker pull hello-world …

动态创建接口地址

和SpringBoot版本有关系 这里用的boot 2.2.2

嵌入式学习——数据结构(队列)——day49

1. 队列 1. 先进先出 2. 缓冲区——先进先出的队列 高速设备和低速设备利用缓冲区进行协调匹配 3. 串口数据的通信利用队列进行协调 4. 顺序队列——循环队列&#xff08;非重点&#xff09; 5. 链式队列 5.1 创建队列 5.2 入队 5.3 出队 5.4 清空队列 5.5 销毁指针 6.…

[FlareOn5]Ultimate Minesweeper

一切题目&#xff0c;可以运行的&#xff0c;首先就要自己运行一次 运行完毕你会发现这是个扫雷游戏 net dnspy打开 一般没有特别的 我们都是点这花括号 这有个getkey 一眼加加密 然后可以三个方向&#xff1a;动调&#xff0c;改文件&#xff0c;静态找数据写加密脚本 简…

Web渗透:XSS-DOM-based XSS

DOM-based XSS&#xff08;基于DOM的跨站脚本攻击&#xff09;是一种XSS攻击类型&#xff0c;其特点是恶意脚本通过操作文档对象模型&#xff08;DOM&#xff09;直接在客户端执行&#xff0c;而无需经过服务器的处理。这种攻击主要利用客户端JavaScript代码中的漏洞&#xff0…

C++入门超详细解释

C入门 文章目录 C入门框架命名空间 namespace &#xff08;不常用&#xff09;命名空间的使用方式&#xff08;三种&#xff09;using namespace std;\<iostream>coutendlcincout的使用命名冲突缺省参数&#xff08;省钱的省&#xff09;缺省参数分类全缺省参数半缺省参数…

使用阿里开源的Spring Cloud Alibaba AI开发第一个大模型应用

背景 前段时间看到Spring推出了SpringAI&#xff0c;可以方便快速的接入ChatGPT等国外的大模型&#xff0c;现在阿里巴巴也紧追脚步推出了Spring Cloud Alibaba AI&#xff0c;Spring Cloud Alibaba AI 目前基于 Spring AI 0.8.1 版本 API 完成通义系列大模型的接入。通义接入…

Golang | Leetcode Golang题解之第172题阶乘后的零

题目&#xff1a; 题解&#xff1a; func trailingZeroes(n int) (ans int) {for n > 0 {n / 5ans n}return }

Linux系统开机自启动脚本(案例:Raspberry Pi 4B脚本)

前言&#xff1a;本篇博客为手把手教学的 Linux 系统开机自启动脚本教程&#xff0c;且额外包含有 Raspberry Pi 4B 的开机自启动案例。日常工程项目中往往需要 Linux 系统能够自启动一些代码程序&#xff0c;本篇博客利用虚拟机下的 Ubuntu 自启动脚本来进行教学&#xff0c;且…

【PS】提取手写签名

准备工具&#xff1a; 纸张&#xff1a;用于承载签名&#xff1b; 笔&#xff1a;用于签名&#xff1b; 手机&#xff1a;用于拍摄签名&#xff1b; Adobe Photoshop 版本: 12.0.3 (12.0.3x20101211 [20101211.r.1222 2010/12/11:02:00:00 cutoff; r branch]) x32&#xff1a;用…

多路h265监控录放开发-(8)完成摄像机管理的修改和删除功能

xviewer.h public:XViewer(QWidget* parent Q_NULLPTR);//编辑摄像机void SetCam(int index);//121 public slots:void AddCam(); //新增摄像机配置120void SetCam(); //121void DelCam(); //121 private:Ui::XViewerClass ui;QMenu left_menu_; xviewer.cpp void XView…

React的State和setState

如何确地使用 State 不要直接修改 State.修改State应该使用 setState():构造函数是唯一可以给 this.state 赋值的地方 State 与 props 类似&#xff0c;但是 state 是私有的&#xff0c;并且完全受控于当前组件 我们可以在我们的自定义组件中添加私有的State jcode class C…

web-原生Ajax

概念: Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML。 作用: 数据交换:通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。 异步交互:可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页的技术&#xff0c;如…

Introduction to linear optimization 第 2 章课后题答案 11-15

线性规划导论 Introduction to linear optimization (Dimitris Bertsimas and John N. Tsitsiklis, Athena Scientific, 1997)&#xff0c; 这本书的课后题答案我整理成了一个 Jupyter book&#xff0c;发布在网址&#xff1a; https://robinchen121.github.io/manual-introdu…

Kafka第一篇——内部组件概念架构启动服务器zookeeper选举以及底层原理

目录 引入 ——为什么分布式系统需要用第三方软件&#xff1f; JMS 对比 组件 架构推演——备份实现安全可靠 &#xff0c; Zookeeper controller的选举 controller和broker底层通信原理 BROKER内部组件 ​编辑 topic创建 引入 ——为什么分布式系统需要用第三方软件&#…

游戏本地化以拓展海外市场

Logrus IT Korea的总监元庆燕&#xff08;KyoungYeon Won&#xff09;发表了一场关于“游戏本地化”的讲座&#xff0c;讲述了独立游戏开发者如何在梦想拓展海外市场的过程中&#xff0c;正确地本地化他们的游戏以满足国际市场的期望&#xff0c;以及实现这一重要任务的过程。 …