C++ 日志系统实战第三步:熟悉掌握各种设计模式

全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的项目笔记吧~   

相关技术知识补充,也是最后的补充知识了~        下文将加入项目代码编写!


目录

设计模式

单例模式

饿汉模式 

懒汉模式

工厂模式

简单工厂模式

工厂方法模式

抽象工厂模式

建造者模式

代理模式


设计模式

        设计模式是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

六大原则:

  1. 单一职责原则(Single Responsibility Principle)
    • 原则内容类的职责应该单一,一个方法只做一件事。职责划分清晰,每次改动到最小单位的方法或类。
    • 使用建议:两个完全不一样的功能不应该放一个类中,一个类中应该是一组相关性很高的函数、数据的封装。
    • 用例:网络聊天:网络通信 & 聊天,应该分割成为网络通信类 & 聊天类。
  2. 开闭原则(Open Closed Principle)
    • 原则内容对扩展开放,对修改封闭。
    • 使用建议:对软件实体的改动,最好用扩展而非修改的方式。
    • 用例:超时卖货:商品价格 - 不是修改商品的原来价格,而是新增促销价格。
  3. 里氏替换原则(Liskov Substitution Principle)
    • 原则内容:通俗点讲,就是只要父类能出现的地方,子类就可以出现,而且替换为子类也不会产生任何错误或异常。在继承类时,务必重写父类中所有的方法,尤其需要注意父类的 protected 方法,子类尽量不要暴露自己的 public 方法供外界调用 。
    • 使用建议:子类必须完全实现父类的方法,子类可以有自己的个性。覆盖或实现父类的方法时,输入参数可以被放大,输出可以缩小。
    • 用例:跑步运动员类 - 会跑步,子类长跑运动员 - 会跑步且擅长长跑,子类短跑运动员 - 会跑步且擅长短跑。
  4. 依赖倒置原则(Dependence Inversion Principle)
    • 原则内容高层模块不应该依赖低层模块,两者都应该依赖其抽象,不可分割的原子逻辑就是低层模式,原子逻辑组装成的就是高层模块。模块间依赖通过抽象(接口)发生,具体类之间不直接依赖。
    • 使用建议:每个类都尽量有抽象类,任何类都不应该从具体类派生。尽量不要重写基类的方法。结合里氏替换原则使用。
    • 用例:奔驰车司机类 - 只能开奔驰; 司机类 - 给什么车,就开什么车; 开车的人:司机 - 依赖于抽象。
  5. 迪米特法则(Law of Demeter),又叫 “最少知道法则”
    • 原则内容:尽量减少对象之间的交互,从而减小类之间的耦合。一个对象应该对其他对象有最少的了解。对类的低耦合提出了明确的要求:只和直接的朋友交流,朋友之间也是有距离的。自己的就是自己的(如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中 )。
    • 使用建议:无特殊额外建议。
    • 用例:老师让班长点名 - 老师给班长一个名单,班长完成点名勾选,返回结果,而不是班长点名,老师勾选。
  6. 接口隔离原则(Interface Segregation Principle)
    • 原则内容:客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。
    • 使用建议:接口设计尽量精简单一,但是不要对外暴露没有实际意义的接口。
    • 用例:修改密码,不应该提供修改用户信息接口,而就是单一的最小修改密码接口,更不要暴露数据库操作。

        从整体上来理解六大设计原则,可以简要的概括为一句话,用抽象构建框架,用实现扩展细节,具体到每一条设计原则,则对应一条注意事项:

  • 单一职责原则告诉我们实现类要职责单一;
  • 里氏替换原则告诉我们不要破坏继承体系;
  • 依赖倒置原则告诉我们要面向接口编程;
  • 接口隔离原则告诉我们在设计接口的时候要精简单一;
  • 迪米特法则告诉我们要降低耦合;
  • 开闭原则是总纲,告诉我们要对扩展开放,对修改关闭。

  我们了解设计模式之后,我们来了解具体的设计模式吧


单例模式

  1. 单例模式的定义:“一个类只能创建一个对象,即单例模式。该设计模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享”。这表明单例模式是一种特殊的设计模式,在这种模式下,一个类在整个程序运行过程中只能有一个对象(实例)存在。并且有一个固定的、全局都能访问到的方式去获取这个唯一的实例,同时这个实例会被程序里的各个模块所使用。

  2. 举例说明:“在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理” 。

    • 在这个服务器程序的场景里,服务器的配置信息都在一个文件中。
    • 我们创建一个单例对象,这个单例对象的职责就是去读取这个配置文件里的信息。由于它是单例的,整个程序中只有这么一个对象能做这件事,保证了配置信息读取的唯一性和一致性。
    • 而服务进程中的其他对象,如果它们需要用到这些配置信息,不需要各自去读取文件(这样可能会导致重复读取或者读取不一致等问题),只需要通过访问这个单例对象,就能获取到配置信息。
    • 这样做的好处就是,在比较复杂的服务器程序环境中,对配置信息的管理变得更加简单和高效。因为所有关于配置信息的操作都围绕着这一个单例对象进行,避免了多个对象同时操作配置信息可能带来的混乱。

        例如,想象有多个模块都需要读取数据库的连接配置信息。如果没有单例模式,每个模块可能都要去读取配置文件,可能会出现读取错误、配置不一致等问题。而使用单例模式,有一个专门的单例对象读取配置文件中的数据库连接信息,其他模块都从这个单例对象获取信息,就能保证整个系统使用的数据库连接配置是一致的,也便于对配置进行修改和维护。

单例模式有两种实现模式:饿汉模式和懒汉模式。

饿汉模式 

在 C++ 里,饿汉模式是单例模式的一种实现方式。单例模式的目的是保证一个类仅有一个实例,并提供一个全局访问点来获取该实例。

饿汉模式的特点

  • 实例创建时机:在程序开始运行时就创建单例类的实例,而不是在首次使用时才创建。“饿汉” 就像一个饥饿的人,迫不及待地创建实例。
  • 线程安全:由于实例在程序启动时就已经创建好,所以不存在多线程环境下同时创建多个实例的问题,天生具备线程安全性。

示例代码

下面是一个简单的饿汉模式实现示例:

#include <iostream>class Singleton {
private:// 构造函数私有化,防止外部创建对象Singleton() {}// 拷贝构造函数私有化,防止拷贝Singleton(const Singleton&) = delete;// 赋值运算符私有化,防止赋值Singleton& operator=(const Singleton&) = delete;// 静态成员变量,存储单例实例static Singleton instance;public:// 静态成员函数,用于获取单例实例static Singleton& getInstance() {return instance;}void doSomething() {std::cout << "Singleton is doing something." << std::endl;}
};// 初始化静态成员变量
Singleton Singleton::instance;int main() {// 获取单例实例Singleton& singleton = Singleton::getInstance();// 调用实例的方法singleton.doSomething();return 0;
}

代码解释

  1. 构造函数私有化Singleton 类的构造函数被声明为私有,这样外部代码就无法直接创建 Singleton 类的对象。
  2. 静态成员变量static Singleton instance; 是一个静态成员变量,用于存储单例实例。在类外进行了初始化 Singleton Singleton::instance;,这确保了在程序启动时就创建了实例。
  3. 静态成员函数static Singleton& getInstance() 是一个静态成员函数,用于获取单例实例。由于它是静态的,可以直接通过类名调用。
  4. 拷贝构造函数和赋值运算符删除:为了防止通过拷贝或赋值操作创建新的实例,将拷贝构造函数和赋值运算符声明为 delete

使用步骤

  1. 定义单例类:按照上述示例,将构造函数、拷贝构造函数和赋值运算符私有化,并定义一个静态成员变量和一个静态成员函数。
  2. 初始化静态成员变量:在类外对静态成员变量进行初始化。
  3. 获取单例实例:通过调用静态成员函数 getInstance() 来获取单例实例,并调用实例的方法。

        饿汉模式的优点是实现简单且线程安全,但缺点是如果单例实例占用资源较多,而程序可能并不需要使用该实例,会造成资源浪费。


懒汉模式

在 C++ 中,懒汉模式同样是单例模式的一种实现方式。单例模式的核心是确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。

懒汉模式的特点

  • 实例创建时机:懒汉模式与饿汉模式不同,它是在首次使用单例实例时才进行创建。就像一个 “懒人”,不到万不得已不会去创建实例。
  • 线程安全问题:在多线程环境下,如果不进行特殊处理,可能会出现多个线程同时判断实例未创建,从而各自创建一个实例的情况,导致单例模式失效。所以,在多线程环境中使用懒汉模式需要考虑线程安全问题。

简单懒汉模式(非线程安全)

以下是一个简单的非线程安全的懒汉模式实现示例:

#include <iostream>class Singleton {
private:// 构造函数私有化,防止外部创建对象Singleton() {}// 拷贝构造函数私有化,防止拷贝Singleton(const Singleton&) = delete;// 赋值运算符私有化,防止赋值Singleton& operator=(const Singleton&) = delete;// 静态成员指针,用于存储单例实例static Singleton* instance;public:// 静态成员函数,用于获取单例实例static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}void doSomething() {std::cout << "Singleton is doing something." << std::endl;}
};// 初始化静态成员指针
Singleton* Singleton::instance = nullptr;int main() {// 获取单例实例Singleton* singleton = Singleton::getInstance();// 调用实例的方法singleton->doSomething();return 0;
}

代码解释

  1. 构造函数私有化Singleton 类的构造函数被声明为私有,这样外部代码就无法直接创建 Singleton 类的对象。
  2. 静态成员指针static Singleton* instance; 是一个静态成员指针,用于存储单例实例。初始化为 nullptr,表示还未创建实例。
  3. 静态成员函数static Singleton* getInstance() 是一个静态成员函数,用于获取单例实例。在函数内部,首先检查 instance 是否为 nullptr,如果是,则创建一个新的 Singleton 实例;否则,直接返回已有的实例。
  4. 拷贝构造函数和赋值运算符删除:为了防止通过拷贝或赋值操作创建新的实例,将拷贝构造函数和赋值运算符声明为 delete

线程安全的懒汉模式(双重检查锁定)

在多线程环境中,可以使用双重检查锁定(Double-Checked Locking)来实现线程安全的懒汉模式:

#include <iostream>
#include <mutex>class Singleton {
private:// 构造函数私有化,防止外部创建对象Singleton() {}// 拷贝构造函数私有化,防止拷贝Singleton(const Singleton&) = delete;// 赋值运算符私有化,防止赋值Singleton& operator=(const Singleton&) = delete;// 静态成员指针,用于存储单例实例static Singleton* instance;// 静态互斥锁,用于线程同步static std::mutex mtx;public:// 静态成员函数,用于获取单例实例static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mtx);if (instance == nullptr) {instance = new Singleton();}}return instance;}void doSomething() {std::cout << "Singleton is doing something." << std::endl;}
};// 初始化静态成员指针
Singleton* Singleton::instance = nullptr;
// 初始化静态互斥锁
std::mutex Singleton::mtx;int main() {// 获取单例实例Singleton* singleton = Singleton::getInstance();// 调用实例的方法singleton->doSomething();return 0;
}

代码解释

  1. 双重检查:在 getInstance() 函数中,首先进行一次非锁定检查,判断 instance 是否为 nullptr。如果不为 nullptr,则直接返回实例,避免了不必要的锁竞争。如果为 nullptr,则加锁进行第二次检查,确保在加锁期间没有其他线程已经创建了实例。
  2. 互斥锁:使用 std::mutex 来实现线程同步,确保在同一时间只有一个线程可以进入临界区创建实例。

使用步骤

  1. 定义单例类:按照上述示例,将构造函数、拷贝构造函数和赋值运算符私有化,并定义一个静态成员指针和一个静态成员函数。
  2. 初始化静态成员指针:在类外对静态成员指针进行初始化,初始值为 nullptr
  3. 获取单例实例:通过调用静态成员函数 getInstance() 来获取单例实例,并调用实例的方法。

        懒汉模式的优点是在需要时才创建实例,避免了不必要的资源浪费;缺点是实现相对复杂,特别是在多线程环境中需要考虑线程安全问题。


工厂模式

        工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象时不会对上层暴露创建逻辑,而是通过使用一个共同结构来指向新创建的对象,以此实现创建 - 使用的分离。

工厂模式可以分为:

简单工厂模式

  • 简单工厂模式:简单工厂模式实现由一个工厂对象通过类型决定创建出来指定产品类的实例。假设有个工厂能生产出水果,当客户需要产品的时候明确告知工厂生产哪类水果,工厂需要接收用户提供的类别信息,当新增产品的时候,工厂内部去添加新产品的生产方式。

简单工厂模式代码示例

// 简单工厂模式: 通过参数控制可以生产任何产品
// 优点: 简单粗暴,直观易懂。使用一个工厂生产同一等级结构下的任意产品
// 缺点:
// 1. 所有东西生产在一起,产品太多会导致代码量庞大
// 2. 开闭原则遵循(开放拓展,关闭修改)的不是太好,要新增产品就必须修改工厂方法。#include <iostream>
#include <string>
#include <memory>class Fruit {
public:Fruit() {}virtual void show() = 0;
};class Apple : public Fruit {
public:Apple() {}virtual void show() {std::cout << "我是一个苹果" << std::endl;}
};class Banana : public Fruit {
public:Banana() {}virtual void show() {std::cout << "我是一个香蕉" << std::endl;}
};class FruitFactory {
public:static std::shared_ptr<Fruit> create(const std::string &name) {if (name == "苹果") {return std::make_shared<Apple>();} else if (name == "香蕉") {return std::make_shared<Banana>();}return std::shared_ptr<Fruit>();}
};int main() {std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");fruit->show();fruit = FruitFactory::create("香蕉");fruit->show();return 0;
}

        这个模式的结构和管理产品对象的方式十分简单,但是它的扩展性非常差,当我们需要新增产品的时候,就需要去修改工厂类新增一个类型的产品创建逻辑,违背了开闭原则。

工厂方法模式

工厂方法模式是在简单工厂模式基础上的扩展,在该模式下新增多个工厂,每个产品对应一个工厂。例如有 A、B 两种产品,就分别开设工厂 A 负责生产产品 A ,工厂 B 负责生产产品 B 。用户只需知道产品对应的工厂名,无需了解具体产品信息,工厂也只需专注生产,无需接收客户对产品类别的指定。

代码示例及注释

#include <iostream>
#include <string>
#include <memory>// 水果基类,定义了抽象方法show(),后续具体水果类需实现该方法,用于展示水果相关信息
class Fruit {
public:Fruit() {}virtual void show() = 0;
};// 苹果类,继承自Fruit基类,实现了show()方法,用于输出苹果相关信息
class Apple : public Fruit {
public:Apple() {}virtual void show() {std::cout << "我是一个苹果" << std::endl;}
private:std::string _color; // 私有成员变量,可用于表示苹果颜色等信息
};// 香蕉类,继承自Fruit基类,实现了show()方法,用于输出香蕉相关信息
class Banana : public Fruit {
public:Banana() {}virtual void show() {std::cout << "我是一个香蕉" << std::endl;}
private:std::string _color; // 私有成员变量,可用于表示香蕉颜色等信息
};// 水果工厂抽象基类,定义了抽象的create()方法,用于创建水果对象,具体实现由子类完成
class FruitFactory {
public:virtual std::shared_ptr<Fruit> create() = 0;
};// 苹果工厂类,继承自FruitFactory,重写create()方法,用于创建苹果对象
class AppleFactory : public FruitFactory {
public:virtual std::shared_ptr<Fruit> create() {return std::make_shared<Apple>(); // 创建并返回一个苹果对象的智能指针}
};// 香蕉工厂类,继承自FruitFactory,重写create()方法,用于创建香蕉对象
class BananaFactory : public FruitFactory {
public:virtual std::shared_ptr<Fruit> create() {return std::make_shared<Banana>(); // 创建并返回一个香蕉对象的智能指针}
};int main() {// 创建一个指向AppleFactory对象的智能指针,这里使用AppleFactory来生产苹果std::shared_ptr<FruitFactory> factory(new AppleFactory());// 通过苹果工厂创建一个水果对象(实际是苹果对象)std::shared_ptr<Fruit> fruit = factory->create();// 调用苹果对象的show()方法,输出"我是一个苹果"fruit->show();// 重置factory指针,使其指向BananaFactory对象,准备生产香蕉factory.reset(new BananaFactory());// 通过香蕉工厂创建一个水果对象(实际是香蕉对象)fruit = factory->create();// 调用香蕉对象的show()方法,输出"我是一个香蕉"fruit->show();return 0;
}

模式优缺点

  • 优点
    • 减轻了工厂类的负担,将某类产品的生产交给指定的工厂来进行 。
    • 开闭原则遵循较好,添加新产品只需要新增产品的工厂即可,不需要修改原先的工厂类 。
  • 缺点:对于某种可以形成一组产品族的情况处理较为复杂,需要创建大量的工厂类。每次增加一个产品时,都需要增加一个具体产品类和工厂类,这会使得系统中类的个数成倍增加,在一定程度上增加了系统的耦合度。

抽象工厂模式

        抽象工厂模式是在工厂方法模式基础上的进一步拓展。工厂方法模式虽引入工厂等级结构,解决了简单工厂模式中工厂类职责过重问题,但每个工厂只生产一类产品,可能导致系统中工厂类数量庞大,开销增加。抽象工厂模式的基本思想是把一些相关产品组成一个产品族(即位于不同产品等级结构中功能相关联的产品集合 ),由同一个工厂来统一生产。

代码示例及注释

#include <iostream>
#include <string>
#include <memory>// 水果基类,定义抽象方法show(),用于展示水果相关信息,具体由子类实现
class Fruit {
public:Fruit() {}virtual void show() = 0;
};// 苹果类,继承自Fruit基类,实现show()方法,用于输出苹果相关信息
class Apple : public Fruit {
public:Apple() {}virtual void show() {std::cout << "我是一个苹果" << std::endl;}
private:std::string _color; // 私有成员变量,可用于表示苹果颜色等信息
};// 香蕉类,继承自Fruit基类,实现show()方法,用于输出香蕉相关信息
class Banana : public Fruit {
public:Banana() {}virtual void show() {std::cout << "我是一个香蕉" << std::endl;}
private:std::string _color; // 私有成员变量,可用于表示香蕉颜色等信息
};// 动物基类,定义抽象方法voice(),用于发出动物叫声,具体由子类实现
class Animal {
public:virtual void voice() = 0;
};// 羊类,继承自Animal基类,实现voice()方法,输出羊的叫声
class Lamb : public Animal {
public:void voice() {std::cout << "咩咩咩\n";}
};// 狗类,继承自Animal基类,实现voice()方法,输出狗的叫声
class Dog : public Animal {
public:void voice() {std::cout << "汪汪汪\n";}
};// 工厂抽象基类,定义了创建水果和动物对象的抽象方法,具体实现由子类完成
class Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string &name) = 0;virtual std::shared_ptr<Animal> getAnimal(const std::string &name) = 0;
};// 水果工厂类,继承自Factory,实现创建水果对象的方法
class FruitFactory : public Factory {
public:// 该方法在当前类中不创建动物对象,直接返回空指针virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {return std::shared_ptr<Animal>();}virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {if (name == "苹果") {return std::make_shared<Apple>(); // 创建并返回一个苹果对象的智能指针} else if (name == "香蕉") {return std::make_shared<Banana>(); // 创建并返回一个香蕉对象的智能指针}return std::shared_ptr<Fruit>(); // 若名称不匹配,返回空的智能指针}
};// 动物工厂类,继承自Factory,实现创建动物对象的方法
class AnimalFactory : public Factory {
public:// 该方法在当前类中不创建水果对象,直接返回空指针virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {return std::shared_ptr<Fruit>();}virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {if (name == "小羊") {return std::make_shared<Lamb>(); // 创建并返回一个羊对象的智能指针} else if (name == "小狗") {return std::make_shared<Dog>(); // 创建并返回一个狗对象的智能指针}return std::shared_ptr<Animal>(); // 若名称不匹配,返回空的智能指针}
};// 工厂生产者类,用于根据传入的名称获取对应的工厂对象
class FactoryProducer {
public:static std::shared_ptr<Factory> getFactory(const std::string &name) {if (name == "动物") {return std::make_shared<AnimalFactory>(); // 返回动物工厂对象的智能指针} else {return std::make_shared<FruitFactory>(); // 返回水果工厂对象的智能指针}}
};int main() {// 获取水果工厂对象std::shared_ptr<Factory> fruit_factory = FactoryProducer::getFactory("水果");// 通过水果工厂创建苹果对象并调用其show()方法std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("苹果");fruit->show();// 通过水果工厂创建香蕉对象并调用其show()方法fruit = fruit_factory->getFruit("香蕉");fruit->show();// 获取动物工厂对象std::shared_ptr<Factory> animal_factory = FactoryProducer::getFactory("动物");// 通过动物工厂创建羊对象并调用其voice()方法std::shared_ptr<Animal> animal = animal_factory->getAnimal("小羊");animal->voice();// 通过动物工厂创建狗对象并调用其voice()方法animal = animal_factory->getAnimal("小狗");animal->voice();return 0;
}

模式优缺点

  • 优点
    • 可以将一组相关的产品对象的创建封装在一起,提高了代码的内聚性和可维护性 。
    • 当需要创建多个相关产品时,减少了创建对象的复杂程度 。
  • 缺点
    • 增加新的产品等级结构复杂,若要添加新的产品族,需要对抽象层代码进行较大修改,违背了 “开闭原则” 。

建造者模式

        建造者模式是一种创建型设计模式,它利用多个简单对象逐步构建出一个复杂对象,能够将复杂对象的构建过程和其表示形式相分离,为创建对象提供了一种优化方式,主要用于应对对象构建过程过于繁杂的情况。

建造者模式的四个核心类

  1. 抽象产品类:定义了产品的抽象属性和行为。
  2. 具体产品类:是具体的产品对象类,继承自抽象产品类,实现了具体的产品功能。
  3. 抽象 Builder 类:为创建产品对象所需的各个部件定义了抽象接口。
  4. 具体产品的 Builder 类:实现抽象 Builder 类的接口,负责具体构建各个部件。
  5. 指挥者 Director 类:统一管理组装过程,提供给调用者使用,通过指挥者来构建完整的产品。

       下面通过一个简单的电脑组装示例,使用 C++ 代码来解释建造者模式。在这个示例中,我们要构建不同配置的电脑,包括主板和显示器等组件。

#include <iostream>
#include <memory>/*抽象电脑类*/
class Computer {public:using ptr = std::shared_ptr<Computer>;Computer() {}void setBoard(const std::string &board) {_board = board;}void setDisplay(const std::string &display) {_display = display;}virtual void setOs() = 0;std::string toString() {std::string computer = "Computer:{\n";computer += "\tboard=" + _board + ",\n"; computer += "\tdisplay=" + _display + ",\n"; computer += "\tOs=" + _os + ",\n"; computer += "}\n";return computer;}protected:std::string _board;std::string _display;std::string _os;
};/*具体产品类*/
class MacBook : public Computer {public:using ptr = std::shared_ptr<MacBook>;MacBook() {}virtual void setOs() {_os = "Max Os X12";}
};/*抽象建造者类:包含创建一个产品对象的各个部件的抽象接口*/
class Builder {public:using ptr = std::shared_ptr<Builder>;virtual void buildBoard(const std::string &board) = 0;virtual void buildDisplay(const std::string &display) = 0;virtual void buildOs() = 0;virtual Computer::ptr build() = 0;
};/*具体产品的具体建造者类:实现抽象接口,构建和组装各个部件*/
class MackBookBuilder : public Builder {public:using ptr = std::shared_ptr<MackBookBuilder>;MackBookBuilder(): _computer(new MacBook()) {}virtual void buildBoard(const std::string &board) {_computer->setBoard(board);}virtual void buildDisplay(const std::string &display) {_computer->setDisplay(display);}virtual void buildOs() {_computer->setOs();}virtual Computer::ptr build() {return _computer;}private:Computer::ptr _computer;
};/*指挥者类,提供给调用者使用,通过指挥者来获取产品*/
class Director {public:Director(Builder* builder):_builder(builder){}void construct(const std::string &board, const std::string &display) {_builder->buildBoard(board);_builder->buildDisplay(display);_builder->buildOs();}private:Builder::ptr _builder;
};int main()
{Builder *buidler = new MackBookBuilder();std::unique_ptr<Director> pd(new Director(buidler));pd->construct("英特尔主板", "VOC显示器");Computer::ptr computer = buidler->build();std::cout << computer->toString();return 0;
}

 代码解释

  • 抽象产品类 Computer

    • 定义了电脑的基本属性,包括主板(_board)、显示器(_display)和操作系统(_os)。
    • 提供了设置主板和显示器的方法 setBoard 和 setDisplay
    • 包含一个纯虚函数 setOs,这使得 Computer 成为抽象类,具体的电脑产品类需要实现该方法来设置操作系统。
    • toString 方法用于将电脑的配置信息格式化为字符串并返回,方便输出展示。
  • 具体产品类 MacBook

    • 继承自 Computer 类,实现了 setOs 方法,将操作系统设置为 "Max Os X12"
  • 抽象建造者类 Builder

    • 定义了创建电脑各个部件的抽象接口,包括 buildBoard(构建主板)、buildDisplay(构建显示器)、buildOs(构建操作系统)以及 build(获取构建好的电脑对象)。
  • 具体产品的具体建造者类 MackBookBuilder

    • 继承自 Builder 类,在构造函数中创建了一个 MacBook 对象。
    • 实现了 buildBoardbuildDisplay 和 buildOs 方法,分别调用 MacBook 对象的相应设置方法来构建电脑的各个部件。
    • build 方法返回构建好的 MacBook 对象。
  • 指挥者类 Director

    • 接收一个 Builder 对象,通过 construct 方法调用 Builder 对象的构建方法,完成电脑的组装过程。
  • main 函数

    • 创建了一个 MackBookBuilder 对象。
    • 创建 Director 对象并传入 MackBookBuilder 对象。
    • 调用 Director 的 construct 方法,传入主板和显示器的信息,完成电脑的构建。
    • 调用 MackBookBuilder 的 build 方法获取构建好的电脑对象。
    • 输出电脑的配置信息。

        通过这种方式,建造者模式将电脑的构建过程和表示分离,使得我们可以方便地构建不同配置的电脑,同时也提高了代码的可维护性和可扩展性。


代理模式

 

        代理模式指代理控制对其他对象的访问,也就是代理对象控制对原对象的引用。在某些情况下,一个对象不适合或者不能直接被引用访问,而代理对象可以在客户端和目标对象之间起到中介的作用。

        代理模式的结构包括一个是真正的你要访问的对象 (目标类)、一个是代理对象。目标对象与代理对象实现同一个接口,先访问代理类再通过代理类访问目标对象。代理模式分为静态代理、动态代理:

  • 静态代理指的是,在编译时就已经确定好了代理类和被代理类的关系。也就是说,在编译时就已经确定了代理类要代理的是哪个被代理类。
  • 动态代理指的是,在运行时才动态生成代理类,并将其与被代理类绑定。这意味着,在运行时才能确定代理类要代理的是哪个被代理类。

        以租房为例,房东将房子租出去,但是要租房子出去,需要发布招租启示,带人看房,负责维修,这些工作中有些操作并非房东能完成,因此房东为了图省事,将房子委托给中介进行租赁。代理模式实现:

// 以房东通过中介租房子为例理解代理模式
#include <iostream>
#include <string>// 抽象接口类,定义租房的抽象行为
class RentHouse {
public:// 纯虚函数,用于定义租房的行为,具体实现由子类完成virtual void rentHouse() = 0; 
};// 房东类,继承自RentHouse接口,代表真实的目标对象
class Landlord : public RentHouse {
public:// 实现RentHouse接口中的rentHouse方法,描述房东将房子租出去的行为void rentHouse() {std::cout << "将房子租出去\n"; }
};// 中介类,继承自RentHouse接口,作为代理对象
class Intermediary : public RentHouse {
public:// 实现RentHouse接口中的rentHouse方法void rentHouse() {// 中介在代理租房前可以添加一些额外操作,比如发布招租信息等std::cout << "中介发布招租信息\n"; // 调用房东实际的租房操作_landlord.rentHouse(); // 中介在代理租房后可以添加一些额外操作,比如安排后续事宜等std::cout << "中介安排后续事宜\n"; }
private:// 持有房东对象,用于在代理行为中调用房东的实际租房操作Landlord _landlord; 
};int main() {Intermediary intermediary;intermediary.rentHouse();return 0;
}

 如果你对日志系统感到兴趣,欢迎关注我👉【A charmer】

后续我将继续带你实现日志系统~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源作业调度框架Quartz框架详细使用说明

Quartz框架详细使用说明 Quartz 是一个功能强大的开源作业调度框架&#xff0c;广泛用于在Java应用程序中执行定时任务。以下是Quartz框架的详细使用说明、完整代码示例、同类框架对比以及总结表格。 1. Quartz框架概述 特点&#xff1a; 灵活的调度&#xff1a;支持多种调度方…

Java实现背景图片加自适应水印图片

由于每张图片的宽高比例不一致&#xff0c;希望使得水印在每张图上的尺寸可以跟随背景图变化&#xff0c;自动调整水印大小。避免每张背景的图所显示的logo水印不至于那么突兀。 一、导入所需Jar包 <dependency><groupId>cn.hutool</groupId><artifactId…

代理ip和实际ip的区别和联系

在互联网时代&#xff0c;IP地址是每个设备连接网络的“身份证”&#xff0c;但你是否知道IP地址还分为‌代理IP‌和‌实际IP‌&#xff1f;它们各自扮演什么角色&#xff1f;为什么有人选择使用代理IP&#xff1f;实际IP又有哪些不可替代的作用&#xff1f;本文将深入解析代理…

mybatis-plus里的com.baomidou.mybatisplus.core.override.MybatisMapperProxy 类的详细解析

以下是 com.baomidou.mybatisplus.core.override.MybatisMapperProxy 类的详细解析&#xff1a; 1. 类的作用 MybatisMapperProxy 是 MyBatis-Plus 框架中用于实现 Mapper 接口动态代理的核心类。它继承自 MyBatis 的 MapperProxy&#xff0c;并扩展了以下功能&#xff1a; …

Memcached 主主复制架构搭建与 Keepalived 高可用实现

实验目的 掌握基于 repcached 的 Memcached 主主复制配置 实现通过 Keepalived 的 VIP 高可用机制 验证数据双向同步及故障自动切换能力 实验环境 角色IP 地址主机名虚拟 IP (VIP)主节点10.1.1.78server-a10.1.1.80备节点10.1.1.79server-b10.1.1.80 操作系统: CentOS 7 软…

如何成功防护T级超大流量的DDoS攻击

防护T级超大流量的DDoS攻击需要综合技术、架构与运营策略的多层次防御体系。以下是基于最新技术实践和行业案例总结的关键防护策略&#xff1a; 一、流量清洗与分布式处理 部署流量清洗中心 T级攻击的核心防御依赖于专业的流量清洗技术。通过部署分布式流量清洗集群&#xff0c…

基于SpringAI Alibaba实现RAG架构的深度解析与实践指南

一、RAG技术概述 1.1 什么是RAG技术 RAG&#xff08;Retrieval-Augmented Generation&#xff09;检索增强生成是一种将信息检索技术与生成式AI相结合的创新架构。它通过以下方式实现智能化内容生成&#xff1a; 知识检索阶段&#xff1a;从结构化/非结构化数据源中检索相关…

数字化技术的五个环节:大数据、云计算、人工智能、区块链、移动互联网

在21世纪的科技浪潮中&#xff0c;数字化技术以其强大的生命力和无限的潜力&#xff0c;正逐步重塑着我们的世界。大数据、云计算、人工智能、区块链、移动互联网&#xff0c;这五大数字化技术的环节&#xff0c;如同构建智慧未来的基石&#xff0c;每一方面都承载着推动社会进…

Java Web容器分类及对比

Java Web容器分类及对比 1. 按功能分类 (1) Servlet/JSP容器&#xff08;轻量级&#xff0c;仅支持Web层&#xff09; Tomcat 特点&#xff1a;轻量级、开源、仅支持Servlet/JSP规范&#xff0c;适合Web应用。 使用方式&#xff1a; // 通过Maven依赖启动Spring Boot应用&…

【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f4da;欢迎订阅专栏…

Python在AI虚拟教学视频开发中的核心技术与前景展望

Python在AI虚拟教学视频开发中的核心技术与前景展望 一、引言&#xff1a;AI虚拟教学的技术革新 随着教育数字化转型加速&#xff0c;AI虚拟教学视频凭借个性化、沉浸式体验成为教育科技的新风口。Python以其强大的多模态处理能力、丰富的开源生态和跨领域兼容性&#xff0c;成…

shadcn/radix-ui的tooltip高度定制arrow位置

尝试了半天&#xff0c;后来发现&#xff0c;不支持。。。。。就是不支持 那箭头只能居中 改side和align都没用&#xff0c;下面有在线实例 https://codesandbox.io/p/sandbox/radix-ui-slider-forked-zgn7hj?file%2Fsrc%2FApp.tsx%3A69%2C21 但是呢&#xff0c; 第一如果…

自动清空 maven 项目临时文件,vue 的 node_modules 文件

echo off setlocal enabledelayedexpansion :: vue 的 node_modules 太大 :: maven 打包后的 target 文件也很大&#xff0c; :: 有些项目日志文件也很大&#xff0c;导致磁盘空间不足了&#xff0c; :: 所以写了个脚本&#xff0c;只要配置一下各项目目录&#xff0c; :: 双击…

[Mybatis-plus]

简介 MyBatis-Plus &#xff08;简称 MP&#xff09;是一个 MyBatis的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变。Mybatis-plus官网地址 注意&#xff0c;在引入了mybatis-plus之后&#xff0c;不要再额外引入mybatis和mybatis-spring&#xff0c;避免因为版本…

管理100个小程序-很难吗

20公里的徒步-真难 群里的伙伴发起了一场天目山20公里徒步的活动&#xff0c;想着14公里都轻松拿捏了&#xff0c;思考了30秒后&#xff0c;就借着春风带着老婆孩子就出发了。一开始溪流清澈见底&#xff0c;小桥流水没有人家&#xff1b;青山郁郁葱葱&#xff0c;枯藤老树没有…

大模型工业化元年:GPT-5开启通用AI新纪元,中国技术如何破局?

过去一周&#xff0c;AI领域的焦点无疑是OpenAI发布的GPT-5预览版&#xff0c;以及全球大模型技术从实验室迈向工业化的关键转折。这场变革不仅标志着通用人工智能&#xff08;AGI&#xff09;的进一步逼近&#xff0c;更掀起了全球产业链的竞争与反思。本文将从技术突破、产业…

软考【网络工程师】2023年5月上午题答案解析

1、固态硬盘的存储介质是()。 A 光盘 B 闪存 C 软盘 D 磁盘 答案是 B。 固态硬盘(Solid State Drive),简称 SSD,是用固态电子存储芯片阵列制成的硬盘,其存储介质是闪存(Flash Memory)。闪存具有非易失性,即在断电后仍能保留存储的数据,且读写速度快、抗震性强、能…

【速写】钩子与计算图

文章目录 前向钩子反向钩子的输入反向钩子的输出 前向钩子 下面是一个测试用的计算图的网络&#xff0c;这里因为模型是自定义的缘故&#xff0c;可以直接把前向钩子注册在模型类里面&#xff0c;这样会更加方便一些。其实像以前BERT之类的last_hidden_state以及pool_output之…

高级电影感户外街拍人像摄影后期Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色介绍 高级电影感户外街拍人像摄影后期 Lr 调色&#xff0c;是运用 Adobe Lightroom 软件&#xff0c;对户外街拍的人像照片进行后期处理&#xff0c;以塑造出具有电影质感的独特视觉效果。此调色过程借助 Lr 丰富的工具与功能&#xff0c;从色彩、光影、对比度等多维度着手…

16.QT-Qt窗口-菜单栏|创建菜单栏|添加菜单|创建菜单项|添加分割线|添加快捷键|子菜单|图标|内存泄漏(C++)

Qt窗⼝是通过QMainWindow类来实现的。 QMainWindow是⼀个为⽤⼾提供主窗⼝程序的类&#xff0c;继承⾃QWidget类&#xff0c;并且提供了⼀个预定义的布局。QMainWindow包含⼀个菜单栏&#xff08;menu bar&#xff09;、多个⼯具栏(tool bars)、多个浮动窗⼝&#xff08;铆接部…