c++_基于多种设计模式下的同步&异步日志器系统

1.项目介绍
本项目就是一个日志器系统,目的就是方便程序员在代码开发中进行日志信息的获取,调试代码定位错误排查问题
这个项目主要支持以下几个功能:
多级别日志消息(可以控制日志输出等级)
同步于异步日志
多种落地方向 (如标准输出,指定文件,以大小自动切换的文件) 可扩展
支持多线程写日志
用到的核心技术:
分模块叙述:

项目源代码
2.开发环境
• CentOS 7
• vscode
• g++/gdb
• Makefile
3.核心技术

  1. 类层次设计(继承与多态的应用)
  2. c++11(auto,智能指针,右值引用等)
  3. 双缓冲区
  4. 生产消费模型
  5. 多线程
  6. 设计模式(单例模式,工厂模式,建造者模式,模板模式,代理模式)

4.日志器模块
1.日志等级模块:定义日志的等级 等级有大小区分 可以做到限制等级输出 开发者根据日志等级可以针对性的解决和定位问题

本日志器共分为五个等级
DEBUG, 调试消息
INFO, 消息
WARNING, 警告
ERROR, 发生错误
FATAL, 发生致命错误
OFF 关闭所以日志输出

2.日志消息模块:一条日志所需要的的各种信息总和 类
类中含有 日志等级 时间 线程id 文件名 行号 日志器名称 消息主体

3.日志器消息格式模块:对日志器的日志消息输出格式,得到字符串类型的整体日志消息 我们也缺省提供了一个默认的格式

格式化流程: 我们对每种消息都有对应的格式控制(还是需要使用者看一下使用方法)
%+字符对应一种类型信息 我们对每个格式都有对应的函数 以将日志消息中的对应信息转换为字符串(这里大量使用了继承和多态) 使用时需要传入对应格式的字符串, 我们会对其进行切割 按顺序将对应的格式对应处理方法放到一个vector管理起来 在输出时只需要遍历vector调用方法就可以得到对应的字符串

各种字符代表的消息
%d ⽇期 %T 缩进 %t 线程id %p ⽇志级别 %c ⽇志器名称
%f ⽂件名 %l ⾏号 %m ⽇志消息 %n 换⾏

4.日志器落地模块:将日志消息落地到对应的文件 本模块默认支持三种落地模式 可扩展
采用到了简单工厂模式

将日志器消息格式模块得到的字符串数据写到文件中
默认支持三种方式
标准输出 指定文件 根据文件大小滚动切换文件

5.异步线程模块:异步日志器需要这个模块
异步模式 主线程将得到的字符串交给异步线程 由异步线程调用落地函数

这里的消息传递采用双缓冲区的生产消费模型 实现了读写分离
一个写入缓冲区 由主线程写入 一个落地缓冲区 异步线程将数据取出并写入文件

双缓冲区的应用:
双缓冲区的好处:
提高了并发读写的效率 在多线程情况下 读写操作存在竞争的 读写分离减少了竞争 仅在两个缓冲区交数据的情况下存在线程互斥
提高了数据安全性 避免了读写操作交叉 从而保证了安全性
提高系统稳定性 减少读写阻塞 提高系统稳定性

相较于环形队列
环形队列同时读取 需要相对较平衡的生产和消费 才能高效 节约了空间 内存反复使用
双缓冲区适用于双方速率不一致 消耗空间
当写缓冲区所以数据都写入完毕就交换讲个缓冲区 这样只有两个缓冲区交换时才需要加锁

6.日志器模块:对以上模块的整合
采用建造者模式
日志器分为同步,异步(安全,非安全) 这些日志器有分为 本地,全局
得到日志消息 建造日志消息对象 得到日志消息的字符串 再调用落地模块

异步模式需要调用异步线程模块
实现流程:由工作线程将日志信息交给异步线程 由异步线程实现落地

全局模式下的日志器都要放进日志器管理模块

7.日志器管理模块:管理全局的日志器
采用了单例模式
将全局模式的日志器都记录下来 再上层调用的时候根据日志器名调用对应日志器

8.缓冲区模块
与异步线程的信息交换
维护一片缓冲区 拥有数据写入和取出方法 安全模式下不扩容 极限测试模式下可以进行扩容

9.任志宏&全局接口模块
为了方便用户使用 还需要使用宏封装一个默认的日志器 根据等级直接落地日志
采用代理模式简化用户使用函数的成本 用户只需要传入日志消息本体就行

c++_基于多种设计模式下的同步&异步日志器系统_第1张图片

设计模式

4.1设计模式是什么
设计模式是一种解决方案,他不是语法规定,是一些提高代码开发效率,代码复用性,健壮性,可读性等等的一种方法。

4.2六大原则
单一职责原则:类的职责单一,尽量让一个类中实现的功能的是相似的。
开闭原则:对扩展开放,对修改封闭,需要修改代码时增加接口。
里氏替换原则:子类必须对继承于父类的所有接口进行重写,是的子类可以代替父类。
依赖倒置原则:⾼层模块不应该依赖低层模块,两者都应该依赖其抽象. 不可分割的原⼦逻辑就是低层模式,原⼦逻辑组装成的就是⾼层模块。
迪米特法则(最少知道法则):尽量减少对象之间的交互,从⽽减⼩类之间的耦合。⼀个对象应该对其他对象有最少的了解。
接口隔离原则:客⼾端不应该依赖它不需要的接⼝,类间的依赖关系应该建⽴在最⼩的接⼝上。

单例模式
即在整个程序运行期间,这个类值构建一个类对象。
该设计模式保证只有一个实例,并提供全局均可用于获取这个实例的唯一接口。
例如 开机启动配置文件的读取,本项目中的日志器管理器类就是一个单例模式。

单例模式有两种类型: 懒汉模式,饿汉模式

1.饿汉模式:项目启动直接创建对象,需要使用时直接获取

// 饿汉模式
template<typename T>
class Singleton {
private:
 static Singleton _eton;
private:
 Singleton(){}
 ~Singleton(){}
public:
 Singleton(const Singleton&) = delete;
 Singleton& operator=(const Singleton&) = delete;
 static T& getInstance() 
 {
 return _eton;
 }
};
Singleton Singleton::_eton;

饿汉模式在程序运行时就要创建对象,可能会拖慢程序启动速度,在未使用的单例对象的情况下就构建了对象可能会浪费空间,但胜在其最开始就创建好了对象,所以获取对象不需要加锁 可以并发获取

2.懒汉模式:只有在第一次需要使用实例的时候才创建对象

// 懒汉模式
template <typename T> 
class Singleton { 
private:
 Singleton(){}
 ~Singleton(){}
public: 
 Singleton(const Singleton&) = delete;
 Singleton& operator=(const Singleton&) = delete;
 static T& getInstance() 
 { 
 static Singleton _eton;//c++11以后保证了静态变量构造与析构的线程安全性 所以不用加锁了
 return _eton; 
 } 
};

工厂模式

简单工厂模式

//简单⼯⼚模式:通过参数控制可以⽣产任何产品
// 优点:简单粗暴,直观易懂。使⽤⼀个⼯⼚⽣产同⼀等级结构下的任意产品
// 缺点:
// 1. 所有东西⽣产在⼀起,产品太多会导致代码量庞⼤
// 2. 开闭原则遵循(开放拓展,关闭修改)的不是太好,要新增产品就必须修改⼯⼚⽅法。
#include 
#include 
#include 
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;
}

工厂方法模式
在简单工厂基础上,保证开闭原则,为每个方法建立一个工厂,增加方法的时候只需要增加工厂就行,但是又显得代码冗余。

class FruitFactory {
 public:
 virtual std::shared_ptr<Fruit> create() = 0;
};
class AppleFactory : public FruitFactory {
 public:
 virtual std::shared_ptr<Fruit> create() {
 return std::make_shared<Apple>();
 }
};
class BananaFactory : public FruitFactory {
 public:
 virtual std::shared_ptr<Fruit> create() {
 return std::make_shared<Banana>();
 	}
 }

抽象工厂模式 对于对象划分种类

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> fruit_factory = FactoryProducer::getFactory("⽔
果");
 std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("苹果");
 fruit->show();
 fruit = fruit_factory->getFruit("⾹蕉");
 fruit->show();
 std::shared_ptr<Factory> animal_factory = FactoryProducer::getFactory("动
物");
 std::shared_ptr<Animal> animal = animal_factory->getAnimal("⼩⽺");
 animal->voice();
 animal = animal_factory->getAnimal("⼩狗");
 animal->voice();
 return 0;
}

增加方法种类时依旧违反了开闭原则

建造者模式
建造者以一个个简单的对象经过一定的顺序构建一个复杂的类对象
建造者主要包含 对象类 建造者类 指挥者类

#include 
#include 
/*抽象电脑类*/
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 MacBookBuilder : public Builder {
 public:
 using ptr = 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;
};
/*指挥者类,提供给调⽤者使⽤,通过指挥者来构造复杂产品*/
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;
}

你可能感兴趣的:(c++,设计模式,开发语言)