C++项目实战——基于多设计模式下的同步异步日志系统-⑪-日志器管理类与全局建造者类设计(单例模式)

文章目录

  • 专栏导读
  • 日志器建造者类完善
  • 单例日志器管理类设计思想
  • 单例日志器管理类设计
  • 全局建造者类设计
  • 日志器类、建造者类整理
  • 日志器管理类测试

专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 C++项目——基于多设计模式下的同步与异步日志系统

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法Linux

在这里插入图片描述

日志器建造者类完善

实现了异步日志器设计之后,将异步日志器添加到日志器建造者类当中。

// 1.抽象一个日志器建造者类(完成日志器所需零部件的构建 & 日志器的构建)
//  1.设置日志器类型
//  2.将不同类型的日志器的创建放到同一个日志器建造者类中完成
enum class LoggerType
{LOGGER_SYNC,LOGGER_ASYNC
};
class LoggerBuilder
{
public:LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC),_limit_level(LogLevel::value::DEBUG),_looper_type(AsyncType::ASYNC_SAFE){}void buildLoggerType(LoggerType type) { _logger_type = type; }void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }void buildLoggerName(const std::string &name) { _logger_name = name; }void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }void buildFormatter(const std::string &pattern){_formatter = std::make_shared<Formatter>(pattern);}template <typename SinkType, typename... Args>void buildSink(Args &&...args) // 由用户自己决定落地方式{LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);_sinks.push_back(psink);}virtual Logger::ptr build() = 0;
protected:AsyncType _looper_type;LoggerType _logger_type;std::string _logger_name;std::atomic<LogLevel::value> _limit_level;Formatter::ptr _formatter;std::vector<LogSink::ptr> _sinks;
};
/*2.派生出具体的建造者类---局部日志器的建造者 & 全局日志器的建造者*/
class LocalLoggerBuilder : public LoggerBuilder
{
public:Logger::ptr build() override{assert(_logger_name.empty() == false);if (_formatter.get() == nullptr){_formatter = std::make_shared<Formatter>();}if (_sinks.empty()){buildSink<StdOutSink>();}if (_logger_type == LoggerType::LOGGER_ASYNC){return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);}return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);}
};

单例日志器管理类设计思想

通过局部日志器建造者创建的日志器受到作用域的限制。但是日志的输出,我们希望能够在任意位置。

因此为了突破日志器作用域的限制,我们创建一个日志器管理类,且该类是一个单例类,这样我们就可以在任意位置通过单例管理器单例获取到指定的日志器进行输出了。

基于单例日志器管理类的设计思想,我们对于日志器建造者类进行继承,继承出一个全局日志器建造者类,实现一个日志器在创建完毕后,直接将其添加到单例的日志器管理器当中,以便于能够在任意位置通过日志器名称能够获取到指定的日志器进行输出。

日志器管理器的作用

  • 对所有创建的日志器进行管理
  • 可以在程序的任意位置进,获取相同的单例对象,获取其中的日志器进行日志输出

单例日志器管理类设计

管理的成员:

  • 默认日志器
  • 所管理的日志器数组(使用哈希表,日志器名称为key,日志器对象为value);
  • 互斥锁

提供的操作:

  • 添加日志器管理
  • 判断是否管理了指定名称的日志器
  • 获取指定名称的日志器
  • 获取默认日志器
class LoggerManager
{
public:static LoggerManager& getInstance(){// c++11之后,静态局部变量,编译器在编译的层面实现了线程安全// 当静态局部变量在没有构造完成之前,其他线程进入就会阻塞static LoggerManager eton;return eton;}void addLogger(Logger::ptr &logger){if(hasLogger(logger->name())) return;std::unique_lock<std::mutex> lock(_mutex);_loggers.insert(std::make_pair(logger->name(), logger));}bool hasLogger(const std::string name){std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);if(it == _loggers.end()){return false;}return true;}Logger::ptr getLogger(const std::string name){std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);if(it == _loggers.end()){return Logger::ptr();}return it->second;}Logger::ptr rootLogger(){return _root_logger;}
private:// 构造函数私有化LoggerManager(){std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::LocalLoggerBuilder());builder->buildLoggerName("root");_root_logger = builder->build();_loggers.insert(std::make_pair("root", _root_logger));}
private:std::mutex _mutex;Logger::ptr _root_logger; // 默认日志器std::unordered_map<std::string, Logger::ptr> _loggers // 日志器数组;
};

全局建造者类设计

为了降低用户的使用复杂度,我们提供一个全局日志器建造者类。全局建造者类的设计思想非常简单,即在局部的基础上增加了一个功能:

  • 将日志器添加到单例对象中
class GlobalLoggerBuilder : public LoggerBuilder
{
public:Logger::ptr build() override{assert(_logger_name.empty() == false);if (_formatter.get() == nullptr){_formatter = std::make_shared<Formatter>();}if (_sinks.empty()){buildSink<StdOutSink>();}Logger::ptr logger;if (_logger_type == LoggerType::LOGGER_ASYNC){logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);}else{logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);}LoggerManager::getInstance().addLogger(logger);return logger;}
};

日志器类、建造者类整理

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <cstdarg>
#include <atomic>
#include <thread>
#include <mutex>
#include <unordered_map>namespace LOG
{class Logger{public:using ptr = std::shared_ptr<Logger>;Logger(const std::string &logger_name,LogLevel::value level,Formatter::ptr &formatter,std::vector<LogSink::ptr> &sinks) : _logger_name(logger_name),_limit_level(level),_formatter(formatter),_sinks(sinks.begin(), sinks.end()){}const std::string& name(){ return _logger_name; }void debug(const std::string &file, size_t line, const std::string &fmt, ...){// 通过传入的参数构造出一个日志消息对象, 进行日志格式化,最终落地// 判断当前的日志是否达到了输出等级if (LogLevel::value::DEBUG < _limit_level){return;}// 对fmt格式化字符串和不定参数进行字符串组织, 得到的日志消息字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == 1){std::cout << "vasprintf failed\n";return;}va_end(ap);serialize(LogLevel::value::DEBUG, file, line, res);free(res);}void info(const std::string &file, size_t line, const std::string &fmt, ...){// 通过传入的参数构造出一个日志消息对象, 进行日志格式化,最终落地if (LogLevel::value::INFO < _limit_level){return;}// 对fmt格式化字符串和不定参数进行字符串组织, 得到的日志消息字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == 1){std::cout << "vasprintf failed\n";return;}va_end(ap);serialize(LogLevel::value::INFO, file, line, res);free(res);}void warn(const std::string &file, size_t line, const std::string &fmt, ...){// 通过传入的参数构造出一个日志消息对象, 进行日志格式化,最终落地if (LogLevel::value::WARN < _limit_level){return;}// 对fmt格式化字符串和不定参数进行字符串组织, 得到的日志消息字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == 1){std::cout << "vasprintf failed\n";return;}va_end(ap);serialize(LogLevel::value::WARN, file, line, res);free(res);}void error(const std::string &file, size_t line, const std::string &fmt, ...){// 通过传入的参数构造出一个日志消息对象, 进行日志格式化,最终落地if (LogLevel::value::ERROR < _limit_level){return;}// 对fmt格式化字符串和不定参数进行字符串组织, 得到的日志消息字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == 1){std::cout << "vasprintf failed\n";return;}va_end(ap);serialize(LogLevel::value::ERROR, file, line, res);free(res);}void fatal(const std::string &file, size_t line, const std::string &fmt, ...){// 通过传入的参数构造出一个日志消息对象, 进行日志格式化,最终落地if (LogLevel::value::FATAL < _limit_level){return;}// 对fmt格式化字符串和不定参数进行字符串组织, 得到的日志消息字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == 1){std::cout << "vasprintf failed\n";return;}va_end(ap);serialize(LogLevel::value::FATAL, file, line, res);free(res);}protected:void serialize(LogLevel::value level, const std::string &file, size_t line, char *str){// 构造LogMsg对象LogMsg msg(level, line, file, _logger_name, str);// 通过格式化工具对LogMsg进行格式化, 得到格式化后的日志字符串std::stringstream ss;_formatter->format(ss, msg);// 对日志进行落地log(ss.str().c_str(), ss.str().size());}virtual void log(const char *data, size_t len) = 0;protected:std::mutex _mutex;std::string _logger_name;                  // 日志器名称std::atomic<LogLevel::value> _limit_level; // 限制输出等级Formatter::ptr _formatter;std::vector<LogSink::ptr> _sinks;};class SyncLogger : public Logger{public:SyncLogger(const std::string &logger_name,LogLevel::value level,LOG::Formatter::ptr &formatter,std::vector<LogSink::ptr> &sinks) : Logger(logger_name, level, formatter, sinks){}protected:void log(const char *data, size_t len){std::unique_lock<std::mutex> lock(_mutex);if (_sinks.empty())return;for (auto &sink : _sinks){sink->log(data, len);}}};class AsyncLogger : public Logger{public:AsyncLogger(const std::string &logger_name,LogLevel::value level,LOG::Formatter::ptr &formatter,std::vector<LogSink::ptr> &sinks,AsyncType looper_type): Logger(logger_name, level, formatter, sinks),_looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type)){}// 将数据写入缓冲区 void log(const char *data, size_t len){_looper->push(data, len);}// 设计一个实际落地函数void realLog(Buffer &buf){if (_sinks.empty())return;for (auto &sink : _sinks){sink->log(buf.begin(), buf.readAbleSize());}}private:AsyncLooper::ptr _looper; // 异步工作器};// 1.抽象一个日志器建造者类(完成日志器所需零部件的构建 & 日志器的构建)//  1.设置日志器类型//  2.将不同类型的日志器的创建放到同一个日志器建造者类中完成enum class LoggerType{LOGGER_SYNC,LOGGER_ASYNC};class LoggerBuilder{public:LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC),_limit_level(LogLevel::value::DEBUG),_looper_type(AsyncType::ASYNC_SAFE){}void buildLoggerType(LoggerType type) { _logger_type = type; }void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }void buildLoggerName(const std::string &name) { _logger_name = name; }void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }void buildFormatter(const std::string &pattern){_formatter = std::make_shared<Formatter>(pattern);}template <typename SinkType, typename... Args>void buildSink(Args &&...args) // 由用户自己决定落地方式{LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);_sinks.push_back(psink);}virtual Logger::ptr build() = 0;protected:AsyncType _looper_type;LoggerType _logger_type;std::string _logger_name;std::atomic<LogLevel::value> _limit_level;Formatter::ptr _formatter;std::vector<LogSink::ptr> _sinks;};/*2.派生出具体的建造者类---局部日志器的建造者 & 全局日志器的建造者*/class LocalLoggerBuilder : public LoggerBuilder{public:Logger::ptr build() override{assert(_logger_name.empty() == false);if (_formatter.get() == nullptr){_formatter = std::make_shared<Formatter>();}if (_sinks.empty()){buildSink<StdOutSink>();}if (_logger_type == LoggerType::LOGGER_ASYNC){return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);}return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);}};class LoggerManager{public:static LoggerManager& getInstance(){// c++11之后,静态局部变量,编译器在编译的层面实现了线程安全// 当静态局部变量在没有构造完成之前,其他线程进入就会阻塞static LoggerManager eton;return eton;}void addLogger(Logger::ptr &logger){if(hasLogger(logger->name())) return;std::unique_lock<std::mutex> lock(_mutex);_loggers.insert(std::make_pair(logger->name(), logger));}bool hasLogger(const std::string name){std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);if(it == _loggers.end()){return false;}return true;}Logger::ptr getLogger(const std::string name){std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);if(it == _loggers.end()){return Logger::ptr();}return it->second;}Logger::ptr rootLogger(){return _root_logger;}private:// 构造函数私有化LoggerManager(){std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::LocalLoggerBuilder());builder->buildLoggerName("root");_root_logger = builder->build();_loggers.insert(std::make_pair("root", _root_logger));}private:std::mutex _mutex;Logger::ptr _root_logger; // 默认日志器std::unordered_map<std::string, Logger::ptr> _loggers // 日志器数组;};class GlobalLoggerBuilder : public LoggerBuilder{public:Logger::ptr build() override{assert(_logger_name.empty() == false);if (_formatter.get() == nullptr){_formatter = std::make_shared<Formatter>();}if (_sinks.empty()){buildSink<StdOutSink>();}Logger::ptr logger;if (_logger_type == LoggerType::LOGGER_ASYNC){logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);}else{logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);}LoggerManager::getInstance().addLogger(logger);return logger;}};
}#endif

日志器管理类测试

#include "logger.hpp"void log_test()
{LOG::Logger::ptr logger = LOG::LoggerManager::getInstance().getLogger("async_logger");logger->debug(__FILE__, __LINE__, "%s", "测试日志");logger->info(__FILE__, __LINE__, "%s", "测试日志");logger->warn(__FILE__, __LINE__, "%s", "测试日志");logger->error(__FILE__, __LINE__, "%s", "测试日志");logger->fatal(__FILE__, __LINE__, "%s", "测试日志");size_t count = 0;while(count < 300000){   logger->fatal(__FILE__, __LINE__, "测试日志-%d", count++);}
}
int main()
{   std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::GlobalLoggerBuilder());builder->buildLoggerName("async_logger");builder->buildLoggerLevel(LOG::LogLevel::value::WARN);builder->buildFormatter("[%c]%m%n");builder->buildLoggerType(LOG::LoggerType::LOGGER_ASYNC);builder->buildEnableUnSafeAsync();builder->buildSink<LOG::FileSink>("./logfile/async.log");builder->buildSink<LOG::StdOutSink>();builder->build();log_test();return 0;
}

在这里插入图片描述

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

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

相关文章

从头开始机器学习:线性回归

从头开始机器学习&#xff1a;线性回归 跟随 16 分钟阅读 28月 <> 1 一、说明 本篇实现线性回归的先决知识是&#xff1a;基本线性代数&#xff0c;微积分&#xff08;偏导数&#xff09;、梯度和、Python &#xff08;NumPy&#xff09;&#xff1b;从线性方程入手。 代…

《机器人学导论》——探究未来世界的奇妙之旅

你是否感到生活变得越来越便利、智能&#xff1f;是的&#xff0c;这些都与机器人技术的发展密不可分。而想要更深入地了解机器人技术&#xff0c;一本好的书籍是必不可少的。那么&#xff0c;今天作者要向大家推荐的就是这样一本优秀的机器人学大作——《机器人学导论》。 《…

JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)

基于bilibili狂神说JUC并发编程视频所做笔记 概述 什么是JUC JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务&#xff1a;普通的线程代码中&#xff0c;我们常使用Runnable接口 但Runnable没有返…

ElasticSearch-数据查询

ElasticSearch-数据查询 目录概述需求&#xff1a; 设计思路实现思路分析1.查询某索引下的所有数据2.二、条件查询3.、条件查询方式二4.四、分页查询5.五、格式化数据 六、排序七 其他条件执行的成立的查询九、范围查询 参考资料和推荐阅读 Survive by day and develop by nigh…

基于类电磁机制优化的BP神经网络(分类应用) - 附代码

基于类电磁机制优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于类电磁机制优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.类电磁机制优化BP神经网络3.1 BP神经网络参数设置3.2 类电磁机制算法应用 4…

使用Premiere、PhotoShop和Audition做视频特效

今天接到一个做视频的任务&#xff0c;给一个精忠报国的视频&#xff0c;要求&#xff1a;   ①去掉人声&#xff0c;就是将唱歌的人声去掉&#xff0c;只留下伴奏&#xff1b;   ②截图视频中的横幅&#xff0c;做一个展开的效果&#xff0c;类似卷纸慢慢展开&#xff1b;…

使用 Tkinter Canvas 小部件添加放大镜功能?

一、说明 据我所知&#xff0c;内置的 Tkinter Canvas 类比例不会自动缩放图像。如果您无法使用自定义小部件&#xff0c;则可以缩放原始图像并在调用缩放函数时将其替换在画布上。 二、实现图像放大镜技术细节 我如何将放大和缩小添加到以下脚本中&#xff0c;我想将其绑定到…

【狂神说】HTML详解

目录 1 HTML概述1.1 什么是HTML1.2 HTML发展史1.3 HTML5的优势1.4 W3C标准 2 网页2.1 网页基本信息2.2 网页基本标签2.2.1 标题标签2.2.2 段落标签2.2.3 换行标签2.2.4 水平线标签2.2.5 字体样式标签&#xff1a;粗体、斜体2.2.6 注释和特殊符号 2.3 图像标签2.4 链接标签邮箱链…

高质量床上用品类网站带手机端的pbootcms模板

模板介绍&#xff1a; 这是一个基于PbootCMS内核开发的床上用品类网站模板&#xff0c;专为床上用品、家用纺织类企业设计和开发。它不仅提供了网站界面简洁简单、易于管理的特点&#xff0c;还附带了测试数据&#xff0c;方便用户进行演示和学习。 模板特点&#xff1a; 采用…

【TES720D-KIT】青翼自研基于复旦微FMQL20S400全国产化ARM开发套件(核心板+底板)

TES720D-KIT是专门针对我司TES720D&#xff08;基于复旦微FMQL20S400的全国产化ARM核心板&#xff09;的一套开发套件&#xff0c;它包含1个TES720D核心板&#xff0c;加上一个TES720D-EXT扩展底板。 FMQL20S400是复旦微电子研制的全可编程融合芯片&#xff0c;在单芯片内集成…

HTTP 原理与CND原理

1 HTTP 原理 HTTP是一个无状态的协议。无状态是指客户机&#xff08;Web浏览器&#xff09;和服务器之间不需要建立持久的连接&#xff0c;这意味着当一个客户端向服务器端发出请求&#xff0c;然后服务器返回响应(response)&#xff0c;连接就被关闭了&#xff0c;在服务器端…

与HTTP相关的各种协议

TCP/IP TCP/IP协议是目前网络世界“事实上”的标准通信协议&#xff0c;实际上是一系列网络通信协议的统称&#xff0c;其中最核心的两个协议是 TCP和IP&#xff0c;其他的还有 UDP、ICMP、ARP 等等&#xff0c;共同构成了一个复杂但有层次的协议栈。 这个协议栈有四层&#x…

前端需要了解的浏览器缓存知识

文章目录 前言为什么需要缓存&#xff1f;DNS缓存缓存读写顺序缓存位置memory cache&#xff08;浏览器本地缓存&#xff09;disk cache&#xff08;硬盘缓存&#xff09;重点&#xff01;&#xff01;&#xff01; 缓存策略 - 强缓存和协商缓存1&#xff09;强缓存ExpiresCach…

城市生命线专题周丨宏电燃气管线智慧化运营解决方案,助力燃气安全运营高质量发展

方案背景 随着我国城市发展建设速度的加快和国家能源结构的调整&#xff0c;天燃气走进了千家万户&#xff0c;燃气门站和城市燃气管网规模越来越庞大。此外&#xff0c;近年燃气泄漏导致的大型爆炸事件频发&#xff0c;给人民的生命安全和财产安全带来灾难性伤害。 行业痛点 …

Android---Android 是如何通过 Activity 进行交互的

相信对于 Android 工程师来说&#xff0c;startActivity 就像初恋一般。要求低&#xff0c;见效快&#xff0c;是每一个菜鸟 Android 工程师迈向高级 Android 工程师的必经阶段。经过这么多年的发展&#xff0c;startActivity 在 google 的调教下已经变得愈发成熟&#xff0c;对…

浅析ArkTS的起源和演进

1 引言 Mozilla创造了JS&#xff0c;Microsoft创建了TS&#xff0c;Huawei进一步推出了ArkTS。 从最初的基础的逻辑交互能力&#xff0c;到具备类型系统的高效工程开发能力&#xff0c;再到融合声明式UI、多维状态管理等丰富的应用开发能力&#xff0c;共同组成了相关的演进脉…

JimuReport 积木报表 v1.6.4 稳定版本正式发布 — 开源免费的低代码报表

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

内容分发网络CDN分布式部署真的可以加速吗?原理是什么?

Cdn快不快&#xff1f;她为什么会快&#xff1f;同样的带宽为什么她会快&#xff1f;原理究竟是什么&#xff0c;同学们本着普及知识的想法&#xff0c;我了解的不是很深入&#xff0c;适合小白来看我的帖子&#xff0c;如果您是大佬还请您指正错误的地方&#xff0c;先谢谢大佬…

[部署网站]01安装宝塔面板搭建WordPress

宝塔面板安装WordPress&#xff08;超详细&#xff09;_Wordpress主题网 参考教程 宝塔面板 - 简单好用的Linux/Windows服务器运维管理面板 官网 1.首先你需要一个服务器或者主机 &#xff08;Windows系统或者Linux系统都可以&#xff09; 推荐Linux系统更稳定&#xff0c;…

设计模式:简单工厂模式(C#、JAVA、JavaScript、C++、Python、Go、PHP):

简介&#xff1a; 简单工厂模式&#xff0c;它提供了一个用于创建对象的接口&#xff0c;但具体创建的对象类型可以在运行时决定。这种模式通常用于创建具有共同接口的对象&#xff0c;并且可以根据客户端代码中的参数或配置来选择要创建的具体对象类型。 在简单工厂模式中&am…