C++基于多设计模式下的同步异步日志系统day1

C++基于多设计模式下的同步&异步日志系统day1

📟作者主页:慢热的陕西人

🌴专栏链接:C++基于多设计模式下的同步&异步日志系统

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

主要内容记录了,日志项目需要的一些前置的补充知识。

在这里插入图片描述

文章目录

  • C++基于多设计模式下的同步&异步日志系统day1
    • 1.项目介绍
      • 1.1项目功能
      • 1.2开发环境
      • 1.3核心技术
      • 1.4环境搭建
    • 2.日志系统的介绍
      • 2.1什么是日志?
      • 2.2为什么需要日志系统
      • 2.3日志系统的技术实现
        • 2.3.1同步写日志
        • 2.3.2异步写日志
    • 3.相关技术知识补充
      • 3.1不定参函数
    • 4.设计模式
      • 4.1六大原则
      • 4.2单例模式
      • 4.3工厂模式
      • 4.4⼯⼚⽅法模式
      • 4.5抽象⼯⼚模式
      • 4.6建造者模式
      • 4.7代理模式

1.项目介绍

1.1项目功能

本项⽬主要实现⼀个⽇志系统,其主要⽀持以下功能:

  • ⽀持多级别⽇志消息
  • ⽀持同步⽇志和异步⽇志
  • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中
  • ⽀持多线程程序并发写⽇志
  • ⽀持扩展不同的⽇志落地⽬标地

1.2开发环境

  • CentOS7
  • vscode/vim
  • g++/gdb
  • Makefile

1.3核心技术

  • 类层次设计(继承和多态的应⽤)
  • C++11(多线程、auto、智能指针、右值引⽤等)
  • 双缓冲区
  • ⽣产消费模型
  • 多线程
  • 设计模式(单例、⼯⼚、代理、模板等)

1.4环境搭建

本项目不依赖于其他任何第三方库,只需要安装好CentOS/Ubuntu+vscode/vim环境即可开发。

2.日志系统的介绍

2.1什么是日志?

**日志:**程序运行过程中所记录的程序运行状态信息。

  • 作用:记录了程序的运行状态信息,以便于程序员能够随时根据状态信息,对系统的运行状态,进行分析

2.2为什么需要日志系统

  • ⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题,可以借助⽇
    志系统来打印⼀些⽇志帮助开发⼈员解决问题
  • 上线客⼾端的产品出现bug⽆法复现并解决,可以借助⽇志系统打印⽇志并上传到服务端帮助开发
    ⼈员进⾏分析
  • 对于⼀些⾼频操作(如定时器、⼼跳包)在少量调试次数下可能⽆法触发我们想要的⾏为,通过断
    点的暂停⽅式,我们不得不重复操作⼏⼗次、上百次甚⾄更多,导致排查问题效率是⾮常低下,可
    以借助打印⽇志的⽅式查问题
  • 在分布式、多线程/多进程代码中,出现bug⽐较难以定位,可以借助⽇志系统打印log帮助定位
    bug
  • 帮助⾸次接触项⽬代码的新开发⼈员理解代码的运⾏流程

2.3日志系统的技术实现

⽇志系统的技术实现主要包括三种类型 :

  • 利⽤printf、std::cout等输出函数将⽇志信息打印到控制台

  • 对于⼤型商业化项⽬,为了⽅便排查问题,我们⼀般会将⽇志输出到⽂件或者是数据库系统⽅便查
    询和分析⽇志,主要分为同步⽇志和异步⽇志⽅式

    a.同步写⽇志

    b.异步写⽇志

2.3.1同步写日志

同步⽇志是指当输出⽇志时,必须等待⽇志输出语句执⾏完毕后,才能执⾏后⾯的业务逻辑语句,⽇
志输出语句与程序的业务逻辑语句将在同⼀个线程运⾏。每次调⽤⼀次打印⽇志API就对应⼀次系统调
⽤write写⽇志⽂件。

image-20240229150136540

在⾼并发场景下,随着⽇志数量不断增加,同步⽇志系统容易产⽣系统瓶颈:

  • ⼀⽅⾯,⼤量的⽇志打印陷⼊等量的write系统调⽤,有⼀定系统开销.
  • 另⼀⽅⾯,使得打印⽇志的进程附带了⼤量同步的磁盘IO,影响程序性能.
2.3.2异步写日志

异步⽇志是指在进⾏⽇志输出时,⽇志输出语句与业务逻辑语句并不是在同⼀个线程中运⾏,⽽是有专⻔的线程⽤于进⾏⽇志输出操作。业务线程只需要将⽇志放到⼀个内存缓冲区中不⽤等待即可继续执⾏后续业务逻辑(作为⽇志的⽣产者),⽽⽇志的落地操作交给单独的⽇志线程去完成(作为⽇志的消费者),这是⼀个典型的⽣产-消费模型

image-20240229151158018

这样做的好处是即使⽇志没有真的地完成输出也不会影响程序的主业务,可以提⾼程序的性能:

  • 主线程调⽤⽇志打印接⼝成为⾮阻塞操作
  • 同步的磁盘IO从主线程中剥离出来交给单独的线程完成

3.相关技术知识补充

3.1不定参函数

在初学C语⾔的时候,我们都⽤过printf函数进⾏打印。其中printf函数就是⼀个不定参函数,在函数内
部可以根据格式化字符串中格式化字符分别获取不同的参数进⾏数据的格式化。

不定参宏函数

#include<iostream>
#include<cstdarg>

#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
//其中最后一个参数__VA_ARGS__,前的##作用是当我们只有一个参数的时候,编译器自动忽略从最后一个","开始的内容;
int main()
{
    LOG("%s-%s", "hello", "西安邮电大学");
    return 0;
}

C风格不定参函数

①一个打印数字的例子:

#include<iostream>
#include<cstdarg>

void printNum(int n, ...)
{
    va_list ap;
    va_start(ap, n); //使得ap指向参数n后面的第一个可变参数
    for(int i = 0; i < n; ++i)
    {
        int num = va_arg(ap, int); //从可变参数中获取每一个整形参数
        std::cout << num << " ";
    }
    std::cout << std::endl;
    va_end(ap); //清空可变参数列表--->将ap置空,防止内存泄漏
}


int main()
{
    printNum(2, 1, 2);
    printNum(5, 78, 25, 37, 43, 55);
    return 0;
}

//运行效果:
//[mi@lavm-5wklnbmaja lesson1]$ ./args 
//1 2 
//78 25 37 43 55 

②实现一个printf

#include<iostream>
#include<cstdarg>

void myprintf(const char* fmt, ...)
{
    char *res;
    va_list ap;
    va_start(ap, fmt); 
    int ret = vasprintf(&res, fmt, ap); //调用vasprintf函数将不定参列表中的参数按照fmt提供的格式,整合成一个字符串;
    if(ret != -1)  //vasprintf函数失败的时候返回-1
    std::cout << res << std::endl;
    va_end(ap);
    free(res);
}

int main()
{
    myprintf("西安邮电大学");
    myprintf("%s-%d", "西安邮电大学",666);
    return 0;
}

//运行效果:
//[mi@lavm-5wklnbmaja lesson1]$ ./args 
//西安邮电大学
//西安邮电大学-666

C++风格不定参函数

#include<iostream>
#include<cstdarg>

void xprintf()
{
    std::cout << std::endl;
}
template<typename T, typename ...Args>
void xprintf(const T & val, Args &&...args) //这里使用右值引用目的是为了使用完美转发
{
    std::cout << val << " ";
    if((sizeof ...(args)) > 0)
    {
        xprintf(std::forward<Args>(args)...); //完美转发,传参的过程中保留对象原生类型属性
    }
    else 
    {
        xprintf();
    }
}

int main()
{
    xprintf("西安邮电大学");
    xprintf("西安邮电大学", "666");
    xprintf("西安邮电大学", "666", "旭日苑");
    return 0;
}

//运行效果
//[root@lavm-5wklnbmaja lesson1]# ./args 
//西安邮电大学 
//西安邮电大学 666 
//西安邮电大学 666 旭日苑 

4.设计模式

4.1六大原则

  • 单一职责原则(SingleResponsibilityPrinciple );

    a.类的职责应该单⼀,⼀个⽅法只做⼀件事。职责划分清晰了,每次改动到最⼩单位的⽅法或类。

    b.使⽤建议:两个完全不⼀样的功能不应该放⼀个类中,⼀个类中应该是⼀组相关性很⾼的函数、数据的封装。

    c.例子:⽹络聊天:⽹络通信&聊天,应该分割成为⽹络通信类&聊天类

  • 开闭原则(OpenClosedPrinciple )

    a.对扩展开放,对修改封闭

    b.使⽤建议:对软件实体的改动,最好⽤扩展⽽⾮修改的⽅式

    c.⽤例:超时卖货:商品价格—不是修改商品的原来价格,⽽是新增促销价格

  • 里氏替换原则(LiskovSubstitutionPrinciple )

    a.通俗点讲,就是只要⽗类能出现的地⽅,⼦类就可以出现,⽽且替换为⼦类也不会产⽣任何错误或异常

    b.在继承类时,务必重写⽗类中所有的⽅法,尤其需要注意⽗类的protected⽅法,⼦类尽量不要暴露⾃⼰的public⽅法供外界调⽤

    c.使⽤建议:⼦类必须完全实现⽗类的⽅法,孩⼦类可以有⾃⼰的个性。覆盖或实现⽗类的⽅法时,输⼊参数可以被放⼤,输出可以缩⼩

    d.⽤例:跑步运动员类-会跑步,⼦类⻓跑运动员-会跑步且擅⻓⻓跑,⼦类短跑运动员-会跑步且擅⻓短跑

  • 依赖倒置原则(Dependence Inversion Principle)

​ a.⾼层模块不应该依赖低层模块,两者都应该依赖其抽象.不可分割的原⼦逻辑就是低层模式,原⼦逻辑组装成的就是⾼层模块

​ b.模块间依赖通过抽象(接⼝)发⽣,具体类之间不直接依赖

​ c.使⽤建议:每个类都尽量有抽象类,任何类都不应该从具体类派⽣。尽量不要重写基类的⽅法。结合⾥⽒替换原则使⽤

​ d.⽤例:奔驰⻋司机类–只能开奔驰;司机类–给什么⻋,就开什么⻋;开⻋的⼈:司机–依赖于抽象

  • 迪⽶特法则(LawofDemeter),⼜叫“最少知道法则”

    a.尽量减少对象之间的交互,从⽽减⼩类之间的耦合。⼀个对象应该对其他对象有最少的了解。
    对类的低耦合提出了明确要求:

    ​ 1.只和直接的朋友交流,朋友之间也是有距离的。⾃⼰的就是⾃⼰的(如果⼀个⽅法放在本类中,既不增加类间关系,也对本类不产⽣负⾯影响,那就放置在本类中)

    b.⽤例:⽼师让班⻓点名–⽼师给班⻓⼀个名单,班⻓完成点名勾选,返回结果,⽽不是班⻓点名,⽼师勾选

  • 接⼝隔离原则(Interface Segregation Principle );

​ a.客⼾端不应该依赖它不需要的接⼝,类间的依赖关系应该建⽴在最⼩的接⼝上

​ b.使⽤建议:接⼝设计尽量精简单⼀,但是不要对外暴露没有实际意义的接⼝

​ c.⽤例:修改密码,不应该提供修改⽤⼾信息接⼝,⽽就是单⼀的最⼩修改密码接⼝,更不要暴露数据库操作

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

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

4.2单例模式

⼀个类只能创建⼀个对象,即单例模式,该设计模式可以保证系统中该类只有⼀个实例,并提供⼀个访问它的全局访问点,该实例被所有程序模块共享。⽐如在某个服务器程序中,该服务器的配置信息存放在⼀个⽂件中,这些配置数据由⼀个单例对象统⼀读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种⽅式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:饿汉模式和懒汉模式

  • 饿汉模式:程序启动时就会创建⼀个唯⼀的实例对象。因为单例对象已经确定,所以⽐较适⽤于多
    线程环境中,多线程获取单例对象不需要加锁,可以有效的避免资源竞争,提⾼性能。
#include<iostream>
#include<cstdarg>

//饿汉
class Singleton
{
private:
    Singleton()
    {
        std::cout << "创建了Singleton对象" << std::endl;
    }
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& Getinstance()
{
    return instance;
}
    ~Singleton()
    {}

private:
    int num;
    static Singleton instance;
};

Singleton Singleton::instance;

int main()
{
    return 0;
}

  • 懒汉模式:第⼀次使⽤要使⽤单例对象的时候创建实例对象。如果单例对象构造特别耗时或者耗费济源(加载插件、加载⽹络资源等),可以选择懒汉模式,在第⼀次使⽤的时候才创建对象。

    a.这⾥介绍的是《EffectiveC++》⼀书作者ScottMeyers提出的⼀种更加优雅简便的单例模式Meyers'SingletoninC++

    b.C++11Staticlocalvariables特性以确保C++11起,静态变量将能够在满⾜thread-safe的前提下唯⼀地被构造和析构。

#include<iostream>
#include<cstdarg>

class Singleton
{
private:
    Singleton()
    {
        std::cout << "创建了Singleton对象" << std::endl;
    }
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;

public:
    static Singleton &Getinstance()
    {
        static Singleton instance;
        return instance;
    }
    ~Singleton()
    {
    }

private:
    int num;
    static Singleton instance;
};

int main()
{
    Singleton::Getinstance();
    Singleton::Getinstance();
    Singleton::Getinstance();
    Singleton::Getinstance();
    return 0;
}

4.3工厂模式

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

工厂模式可以分为:

  • 简单工厂模式:

    简单⼯⼚模式实现由⼀个⼯⼚对象通过类型决定创建出来指定产品类的实例。假设有个⼯⼚能⽣产出⽔果,当客⼾需要产品的时候明确告知⼯⼚⽣产哪类⽔果,⼯⼚需要接收⽤⼾提供的类别信息,当新增产品的时候,⼯⼚内部去添加新产品的⽣产⽅式 。

    优点:简单粗暴,直观易懂。使⽤⼀个⼯⼚⽣产同⼀等级结构下的任意产品

    缺点:1.所有东西⽣产在⼀起,产品太多会导致代码量庞⼤

    ​ 2.开闭原则遵循(开放拓展,关闭修改)的不是太好,要新增产品就必须修改⼯⼚⽅法

#include<iostream>
#include<cstdarg>
#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;
}

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

4.4⼯⼚⽅法模式

在简单⼯⼚模式下新增多个⼯⼚,多个产品,每个产品对应⼀个⼯⼚。假设现在有A、B两种产品,则开两个⼯⼚,⼯⼚A负责⽣产产品A,⼯⼚B负责⽣产产品B,⽤⼾只知道产品的⼯⼚名,⽽不知道具体的产品信息,⼯⼚不需要再接收客⼾的产品类别,⽽只负责⽣产产品。

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:
    virtual std::shared_ptr<Fruit> create() = 0;
};

class AppleFactory : public FruitFactory
{
public:
    std::shared_ptr<Fruit> create() override
    {
        return std::make_shared<Apple>();
    }
};

class BananaFactory : public FruitFactory
{
public:
    std::shared_ptr<Fruit> create() override
    {
        return std::make_shared<Banana>();
    }
};

int main()
{
    std::shared_ptr<FruitFactory> factory(new AppleFactory());
    std::shared_ptr<Fruit>fruit = factory->create();
    fruit->show();
    factory.reset(new BananaFactory());
    fruit = factory->create();
    fruit->show();
    return 0;
}


⼯⼚⽅法模式每次增加⼀个产品时,都需要增加⼀个具体产品类和⼯⼚类,这会使得系统中类的个数
成倍增加,在⼀定程度上增加了系统的耦合度。

4.5抽象⼯⼚模式

⼯⼚⽅法模式通过引⼊⼯⼚等级结构,解决了简单⼯⼚模式中⼯⼚类职责太重的问题,但由于⼯⼚⽅法模式中的每个⼯⼚只⽣产⼀类产品,可能会导致系统中存在⼤量的⼯⼚类,势必会增加系统的开销。此时,我们可以考虑将⼀些相关的产品组成⼀个产品族(位于不同产品等级结构中功能相关联的产品组成的家族),由同⼀个⼯⼚来统⼀⽣产,这就是抽象⼯⼚模式的基本思想。

#include<iostream>
#include<cstdarg>
#include<string>
#include<memory>

class Fruit
{
public:
    Fruit() {}
    virtual void name() = 0;
};

class Apple : public Fruit
{
public:
    Apple() {}
    virtual void name()
    {
        std::cout << "我是一个苹果" << std::endl;
    }
};

class Banana : public Fruit
{
public:
    Banana() {}
    virtual void name()
    {
        std::cout << "我是一个香蕉" << std::endl;
    }
};

class Animal
{
public:
    virtual void name() = 0; //纯虚函数,要求子类必须重写
};

class Lamp : public Animal
{
public:
    virtual void name()
    {
        std::cout << "我是山羊!!" << std::endl;
    }
};

class Dog : public Animal
{
public:
    virtual void name()
    {
        std::cout << "我是小狗!!" << std::endl;
    }
};

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;
};

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>(); //相当于空指针;
    }
};

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<Lamp>();
        }
        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> ff = FactoryProducer::getFactory("水果");
    std::shared_ptr<Fruit> f = ff->GetFruit("香蕉");
    f->name();
    f = ff->GetFruit("苹果");
    f->name();

    std::shared_ptr<Factory> af = FactoryProducer::getFactory("动物");
    std::shared_ptr<Animal> a = af->GetAnimal("山羊");
    a->name();
    a = af->GetAnimal("小狗");
    a->name();

    return 0;
}

//运行结果
//[mi@lavm-5wklnbmaja PreStudy]$ ./args 
//我是一个香蕉
//我是一个苹果
//我是山羊!!
//我是小狗!!

抽象⼯⼚模式适⽤于⽣产多个⼯⼚系列产品衍⽣的设计模式,增加新的产品等级结构复杂,需要对原有系统进⾏较⼤的修改,甚⾄需要修改抽象层代码,违背了“开闭原则”

4.6建造者模式

建造者模式是⼀种创建型设计模式,使⽤多个简单的对象⼀步⼀步构建成⼀个复杂的对象,能够将⼀个复杂的对象的构建与它的表⽰分离,提供⼀种创建对象的最佳⽅式。主要⽤于解决对象的构建过于复杂的问题

建造者模式主要基于四个核⼼类实现:

  • 抽象产品类:
  • 具体产品类:⼀个具体的产品对象类
  • 抽象Builder类:创建⼀个产品对象所需的各个部件的抽象接⼝
  • 具体产品的Builder类:实现抽象接⼝,构建各个部件
  • 指挥者Director类:统⼀组建过程,提供给调⽤者使⽤,通过指挥者来构造产品
#include<iostream>
#include<cstdarg>
#include<string>
#include<memory>

//抽象类
class Computer
{
public:
    using ptr = std::shared_ptr<Computer>; //using 关键字的用法,相当于prt等价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 { \n";
        computer += "\tboard = " + _board + ",\n";
        computer += "\tdisplay = " + _display + ",\n";
        computer += "\tOS = " + _os + ",\n } \n";
        return computer; 
    }

protected:
    std::string _board;
    std::string _display;
    std::string _os;
};

//具体的macbook类继承抽象父类
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 MacBookBuilder : public Builder
{
public:
    using prt = std::shared_ptr<MacBookBuilder>;
    MacBookBuilder(): _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;  //类型是std::shared_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 *builder = new MacBookBuilder(); // 先构造一个具体的电脑builder类
    std::unique_ptr<Director> dp(new Director(builder)); //构造一个指挥者类
    dp->construct("华硕主板", "飞利浦显示器");
    Computer::ptr computer = builder->build(); //将builder构建出来的电脑实例用一个对象指针来接受
    std::cout << computer->toString(); //打印出对应的电脑信息

    return 0;
}

4.7代理模式

代理模式指代理控制对其他对象的访问,也就是代理对象控制对原对象的引⽤。在某些情况下,⼀个对象不适合或者不能直接被引⽤访问,⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。代理模式的结构包括⼀个是真正的你要访问的对象(⽬标类)、⼀个是代理对象。⽬标对象与代理对象实现同⼀个接⼝,先访问代理类再通过代理类访问⽬标对象。代理模式分为静态代理、动态代理:

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

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

//代理模式
class RentHouse
{
public:
    virtual void rentHouse() = 0; //纯虚函数子类必须实现
};

//中介
class Landlord : public RentHouse
{
public:
    void rentHouse()
    {
        std::cout << "将房子租出去\n";
    }
};

//中介代理类
class Intermediary : public RentHouse
{
public:
    void rentHouse()
    {
        std::cout << "发布招租启示\n";
        std::cout << "带人看房\n";
        _landlord.rentHouse();
        std::cout << "负责租后维修\n";
    }

private:
    Landlord _landlord;
};

int main()
{
    Intermediary intermediary;

    intermediary.rentHouse();

    return 0;
}

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

死记硬背spring bean 的生命周期

1.bean的生命周期 我们平常经常使用类似于new Object()的方式去创建对象&#xff0c;在这个对象没有任何引用的时候&#xff0c;会被gc给回收掉。而对于spring而言&#xff0c;它本身存在一个Ioc容器&#xff0c;就是用来管理对象的&#xff0c;而对象的生命周期也完全由这个容…

67-箭头函数,new.target,模版字符串

1.箭头函数 ES6新增语法&#xff0c;用来简化函数的书写()>{} <script>//箭头函数的基本使用let a (a,b)>{return ab;}let c a(1,2);console.log(c);//输出3</script> 2.简写形式&#xff1a; 2.1参数&#xff1a;只有一个参数时可以省略小括号a>{}&…

Java Stream 流?看这一篇就够了!

大家好&#xff0c;从我开始写博客也过去半年多了&#xff0c;c 站陪我走过了学习 Java 最艰苦的那段时光&#xff0c;也非常荣幸写的博客能得到这么多人的喜欢。 某一天当我开始学习分布式的时候突然想到这可能是补充 Java 知识拼图的最后几块部分了&#xff0c;为了将前面的知…

《springcloud alibaba》 三 sentinel流量控制

目录 sentinel准备流控规则 qpspom.xmlapllication.yml启动类controller查看结果流控提示不太友好 流控规则 线程数全局异常处理pom.xmlapplication.yml启动类实体类controller类异常类测试 关联流控模式关联jmeter 链路servicecontroller代码调整 流控效果Warm UP 熔断降级规则…

idea项目中文乱码

背景&#xff1a; 从gitee下download了项目发现配置值文件application.properies中出现了乱码&#xff0c;如下 其他文件都正常&#xff0c;例如 解决&#xff1a; 不要 忘记 ok 解决后配置文件 application.properties

2.1 表结构数据

1、表结构数据 字段&#xff1a;整列数 记录&#xff1a;整行数 维度&#xff1a;业务角度 度量&#xff1a;业务行为结果 维度字段&#xff1a;文本型&#xff08;状态&#xff09; 度量字段&#xff1a;数值型&#xff08;交易结果&#xff09; 2、事实表&维度表 维度表…

ubuntu22.04安裝mysql8.0

官网下载mysql&#xff1a;MySQL :: Download MySQL Community Server 将mysql-server_8.0.20-2ubuntu20.04_amd64.deb-bundle.tar上传到/usr/local/src #解压压缩文件 tar -xvf mysql-server_8.0.20-2ubuntu20.04_amd64.deb-bundle.tar解压依赖包依次输入命令 sudo dpkg -i m…

基于springboot+vue的纺织品企业财务管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

顶刊 Radiology2023 Top10文章排行榜发布:ChatGPT霸占5席!医学前沿必读

顶刊 Radiology2023 Top10文章排行榜发布&#xff1a;ChatGPT霸占5席&#xff01;医学前沿必读 期刊基本信息 期刊名称&#xff1a;RADIOLOGY 期刊ISSN: 0033-8419 影响因子/SCI分区&#xff1a;19.7/1区 出版周期&#xff1a;Monthly Radiology 是医学放射学领域的顶级期刊&am…

【算法】最小生成树—Prim算法与Kruskal算法

Prim算法和Kruskal算法都是解决最小生成树问题的经典算法。最小生成树是原图的最小连通子图&#xff0c;它包含原图的全部结点&#xff0c;且保持图连通的所有边代价和最小。一个连通图可能有多个最小生成树。 一、Prim算法 含义 Prim算法&#xff0c;也被称为普里姆算法&…

Unity(第十七部)Unity自带的角色控制器

组件Character Controller 中文角色控制器 using System.Collections; using System.Collections.Generic; using UnityEngine;public class player : MonoBehaviour {private CharacterController player;void Start(){player GetComponent<CharacterController>();}v…

Win11系统实现adb命令向安卓子系统安装APP

Win11系统实现通过adb命令向安卓子系统安装已下载好的apk包。 要实现以上目标&#xff0c;我们需要用到一个Android SDK 的组件Android SDK Platform-Tools &#xff01;这个组件呢其实是被包含在 Android Studio中的&#xff0c;如果你对安卓开发有所了解对此应该不会陌生&…

jmeter如何请求访问https接口

添加线程组http请求 新建线程组&#xff0c;添加http请求 填入协议&#xff0c;ip&#xff0c;端口&#xff0c;请求类型&#xff0c;路径&#xff0c;以及请求参数&#xff0c;查看结果树等。 然后最关键的一步来了。 导入证书 步骤&#xff1a;获取证书&#xff0c;重新生…

Linux磁盘性能方法以及磁盘io性能分析

Linux磁盘性能方法以及磁盘io性能分析 1. fio压测1.1. 安装fio1.2. bs 4k iodepth 1&#xff1a;随机读/写测试&#xff0c;能反映硬盘的时延性能1.3. bs 128k iodepth 32&#xff1a;顺序读/写测试&#xff0c;能反映硬盘的吞吐性能 2. dd压测2.1. 测试纯写入性能2.2. 测试…

【深度学习】Pytorch 教程(十五):PyTorch数据结构:7、模块(Module)详解(自定义神经网络模型并训练、评估)

文章目录 一、前言二、实验环境三、PyTorch数据结构1、Tensor&#xff08;张量&#xff09;1. 维度&#xff08;Dimensions&#xff09;2. 数据类型&#xff08;Data Types&#xff09;3. GPU加速&#xff08;GPU Acceleration&#xff09; 2、张量的数学运算1. 向量运算2. 矩阵…

《2023跨境电商投诉大数据报告》发布|亚马逊 天猫国际 考拉海购 敦煌网 阿里巴巴

2023年&#xff0c;跨境电商API接口天猫国际、京东国际和抖音全球购以其强大的品牌影响力和市场占有率&#xff0c;稳坐行业前三的位置。同时&#xff0c;各大跨境电商平台消费纠纷问题层出不穷。依据国内知名网络消费纠纷调解平台“电诉宝”&#xff08;315.100EC.CN&#xff…

C++设计模式_创建型模式_工厂方法模式

目录 C设计模式_创建型模式_工厂方法模式 一、简单工厂模式 1.1 简单工厂模式引入 1.2 简单工厂模式 1.3 简单工厂模式利弊分析 1.4 简单工厂模式的UML图 二、工厂方法模式 2.1 工厂模式和简单工厂模式比较 2.2 工厂模式代码实现 2.3 工厂模式UML 三、抽象工厂模式 3.1 战斗场景…

C语言可以干些什么?C语言主要涉及哪些IT领域?

C语言可以干些什么&#xff1f;C语言主要涉及哪些IT领域&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家…

LangChain---大型语言模型(LLM)的标准接口和编程框架

1.背景说明 公司在新的一年规划中突然提出要搞生成式AI(GenAI)的相关东西&#xff0c;在公司分享的参考资料中了解到了一些相关的信息&#xff0c;之所以想到使用LangChain&#xff0c;是因为在应用中遇到了瓶颈问题&#xff0c;除了已经了解和研究过的OpenAI的ChatGpt&#xf…

分层解耦-三层架构(未完)

controller层——》service——》dao——》service——》controller 控制反转 依赖注入