结合项目阐述 设计模式:单例、工厂、观察者、代理
原文链接:https://download.csdn.net/blog/column/12433305/133862792#_1613
1、工厂模式应用
C++17及之后可编译
/*日志落地模块的实现1.抽象落地基类2.派生子类(根据不同落地方向进行派生)3.使用工厂模式进行创建与表示的分离
*/#ifndef __M_SINK_H__
#define __M_SINK_H__//#include "util.hpp"
#include <memory>
#include <fstream>
#include <sstream>
#include <cassert>#include <filesystem>
#include <iostream>namespace lgrlog
{class LogSink{public://ptr有什么用?//ptr是一个智能指针,可以自动管理对象生命周期,不需要手动释放//ptr是一个父类指针,可以指向子类对象,但是不能调用子类独有接口,只能调用父类接口,所以这里创建的是LogSink的智能指针using ptr = std::shared_ptr<LogSink>;// 定义智能指针类型(用强指针类型)// 日志落地接口LogSink() {}virtual ~LogSink() {}virtual void log(const char* data, size_t len) = 0;};// 落地方向类型1:标准输出(控制台)class StdoutSink : public LogSink{public:// 将日志写入到标准输出void log(const char* data, size_t len) override{std::cout.write(data, len);}};// 落地方向类型2:指定文件class FileSink : public LogSink{public:// 构造时传入文件名,并打开文件,将操作句柄管理起来FileSink(const std::string& pathname) :_pathname(pathname){// 1.创建日志文件所在的目录//util::File::createDirectory(util::File::path(pathname));//util::File::createDirectory(util::File::path(pathname));std::filesystem::path path(pathname);if (!std::filesystem::exists(path.parent_path())){std::filesystem::create_directories(path.parent_path());}// 2.创建并打开日志文件_ofs.open(_pathname, std::ios::binary | std::ios::app);assert(_ofs.is_open());}// 将日志写入到指定文件void log(const char* data, size_t len) override{_ofs.write(data, len);assert(_ofs.good());}private:std::string _pathname;std::ofstream _ofs;};// 落地方向类型3:滚动文件(以大小进行滚动)class RollBySizeSink : public LogSink{public:// 构造时传入文件名,并打开文件,将操作句柄管理起来RollBySizeSink(const std::string& basename, size_t max_size):_basename(basename), _max_fsize(max_size), _cur_fsize(0), _name_count(0){std::string pathname = createNewFile();// 1.创建日志文件所在的目录//util::File::createDirectory(util::File::path(pathname));std::filesystem::path path(pathname);if (!std::filesystem::exists(path.parent_path())){std::filesystem::create_directories(path.parent_path());}// 2.创建并打开日志文件_ofs.open(pathname, std::ios::binary | std::ios::app);assert(_ofs.is_open());}// 将日志写入到标准输出--写入时判断文件大小,超过最大大小就要切换文件void log(const char* data, size_t len) override{if (_cur_fsize >= _max_fsize){_ofs.close();std::string pathname = createNewFile();_ofs.open(pathname, std::ios::binary | std::ios::app);assert(_ofs.is_open());_cur_fsize = 0;}_ofs.write(data, len);assert(_ofs.good());_cur_fsize += len;}private:// 进行大小判断,超过指定大小则创建新文件std::string createNewFile(){// 获取系统时间,以时间构造文件扩展名//time_t t = util::Date::now();//struct tm lt;//localtime_r(&t, <);std::stringstream filename;filename << _basename;/*filename << lt.tm_year + 1900;filename << lt.tm_mon + 1;filename << lt.tm_mday;filename << lt.tm_hour;filename << lt.tm_min;filename << lt.tm_sec;*/filename << "-";filename << _name_count++;filename << ".log";return filename.str();}private:// 通过基础文件名 + 扩展文件名(以时间生成)组成一个实际的当前输出文件名size_t _name_count;std::string _basename; // ./log/base- -> ./log/base-20250114110125.logstd::ofstream _ofs;size_t _max_fsize; // 记录最大大小,当前文件超过这个大小就要切换文件size_t _cur_fsize; // 记录当前文件已经写入的数据大小};class SinkFactory{public://SinkType: 落地方向 类型(继承自LogSink的3个派生类之一)//Args...: 构造函数参数类型template<typename SinkType, typename ...Args>static LogSink::ptr create(Args&& ...args)// 这里的Args...表示参数包,可以传入多个参数{//模式:工厂模式// 1.创建对象// 2.返回对象// 3.对象管理由工厂负责,不由调用者管理// 4.对象创建与表示分离 return std::make_shared<SinkType>(std::forward<Args>(args)...);}};
}int main()
{// 1.1创建落地对象 //ptr是一个智能指针,可以自动管理对象生命周期,不需要手动释放//ptr是一个父类指针,可以指向子类对象,但是不能调用子类独有接口,只能调用父类接口,所以这里创建的是LogSink的智能指针lgrlog::LogSink::ptr sink = lgrlog::SinkFactory::create<lgrlog::StdoutSink>();// 1.2落地日志sink->log("hello world", 11);// 落地方向类型1:log标准输出// 2.1 创建另一个落地对象std::string filename = "./log/test.log";lgrlog::LogSink::ptr sink2 = lgrlog::SinkFactory::create<lgrlog::FileSink>(filename);// 2.2 落地日志sink2->log("hello world", 11);// 落地方向类型2:log指定到文件// 3.1 创建另一个落地对象lgrlog::LogSink::ptr sink3 = lgrlog::SinkFactory::create<lgrlog::RollBySizeSink>("./log/roll-by-size", 1024 * 1024);// 3.2 落地日志sink3->log("hello world", 11);// 落地方向类型3:滚动显示logreturn 0;
}#endif
2、