【C++】面向对象项目实战

在前面的学习中,我们已经制作了面向过程的简易日志模块,下面使用面向对象来进行制作

准备工作

可配置

支持配置输出console和文件

支持指定文件路径

支持可指定配置文件

可扩展

支持扩展日志输出

支持扩展日志格式化

安全

工厂类确保生命周期安全

确保后续线程安全可以扩展

面向对象开发原则

开闭原则OCP
定义:

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

目的:

通过扩展而非修改现有代码来实现新功能,降低维护成本。

示例:

不好的设计:直接修改现有类来添加新功能。
好的设计:通过继承或接口实现新功能,而不修改原有代码。

单一职责原则SRP
定义:

一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。

目的:

避免类承担过多职责,导致代码复杂、难以维护。

示例:

不好的设计:一个User类既负责用户信息管理,又负责发送邮件。
好的设计:将用户信息管理和邮件发送分离到不同的类中。

里氏替换原则LSP
定义:

子类必须能够替换其基类,而不会影响程序的正确性。

目的:

确保继承关系的正确性,避免子类破坏基类的行为。

示例:

不好的设计:子类重写父类方法,导致父类的行为被破坏。
好的设计:子类在重写方法时,保持与父类方法的一致性。

依赖倒置原则DIP
定义:

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

目的:

降低模块间的耦合度,提高系统的灵活性。

示例:

不好的设计:高层模块直接调用低层模块的具体实现。
好的设计:通过接口或抽象类来解耦高层和低层模块。

接口隔离原则ISP
定义:

客户端不应该依赖它不需要的接口,即接口应该细化,避免臃肿。

目的:

减少接口的冗余,提高代码的灵活性和可维护性。

示例:

不好的设计:一个Printer接口包含打印、扫描、复印等多种功能,客户端被迫实现所有方法。
好的设计:将Printer接口拆分为多个小接口,如Printable、Scannable、Copyable,客户端只需实现所需的接口。

迪米特原则LOD
定义:

一个对象应该对其他对象有尽可能少的了解,即“只与直接朋友通信”。

目的:

降低类之间的耦合度,提高代码的可维护性。

示例:

不好的设计:一个类直接调用多个其他类的复杂方法。
好的设计:通过中间层(如服务类)来协调对象间的交互。

组合优于继承原则COI
定义:

优先使用组合(一个类包含另一个类的实例)而非继承来实现功能复用。

示例:

不好的设计:通过继承来实现所有功能,导致类层次结构复杂。
好的设计:使用组合来灵活地复用功能,减少类之间的耦合。

日志模块类说明

LogFac

工厂类,将对象的创建过程隐藏在工厂类中,客户端只需调用工厂方法即可获得所需对象。

Logger

组合类,有助于实现代码的模块化、可重用性和灵活性。负责一个完整的功能

LogFormat和LogOutput

委托类,是一种用于封装方法引用的类,核心功能是将方法作为参数传递,是组合类模块实现的零件

XlogFormat和XlogOutput

派生类,实现功能的基础。XlogFormat实现日志的格式化,XlogOutput实现日志的输出

格式化代码实现

log_format.h

log_format.h中声明日志格式化虚函数的接口类

XlogFormat

XlogFormat继承父类日志的格式化虚函数并声明函数

父类接口只需要.h文件,而子类的声明和实现.h和.cpp文件都需要,一个个创建有些麻烦,因此在工具栏/项目/添加类/中一键添加文件

在xlog_format中继承并声明函数

在xlog_format.cpp中做具体的实现

输出代码实现

log_output.h负责声明日志的输出的虚函数接口

添加类

声明

定义函数并实现

输出到文件的代码实现

基类依旧使用log_output.h中的接口类
创建子类文件

继承父类虚函数并进行声明,因为在前面已经声明并实现了Output函数,所以我们要重写该函数

定义文件写入流接口和文件打开函数

接口实现

组合的代码实现

组合类Logger负责委托LogOutput和LogFormat完成功能的实现

添加类

在日志模块中我们要加入日志级别,因此定义一个枚举类型

enum class 是 C++11 引入的一种强类型枚举的语法,用于定义一组具有命名常量的集合。

与传统的 enum 不同,enum class 提供了更强的类型安全性,避免了宏的命名冲突

并限制了不同类型之间枚举值的隐式转换。

定义写日志函数

进行实现

首先实现的是日志类型的转换

然后再.h文件中进行委托

添加对象的指针

用指针进行委托

指针初始化函数

析构函数清理指针,注意放在public访问级别

设置最低日志级别,默认为DEBUG

只输出大于最低日志级别的日志

工厂类

Logger是面向用户的功能接口,LogFac是负责维护Logger对象和Logger的生命周期

添加类

单件模式

初始化函数定义

创建logger对象,并返回出去

初始化函数实现

预处理宏

读取配置文件

资源配置文件(Configuration File)是一种用于存储程序或系统配置信息的文件,通常包含关键参数、设置和选项,用于定义程序或系统的行为、功能和特性。

新建资源配置文件

添加配置如图

新建类

定义一个读取配置文件的函数

定义一个哈希表

std::map 是 C++ 标准模板库(STL)中的一个关联容器,用于存储键值对
<std::string, std::string> 表示键和值的类型都是 std::string

实现读取

在文件读取过程中检查流的状态。如果文件流出现问题(如读取失败、到达文件末尾等),则立即跳出循环。

实现键值存储的接口

const(修饰返回值):
返回值类型是 const std::string&,表示返回一个对 std::string 对象的常量引用。
使用 const 引用可以避免不必要的字符串拷贝,同时保证调用者不能通过返回值修改原始字符串。
std::string&(返回值类型):
返回一个引用,意味着返回的是某个已存在的 std::string 对象的引用,而不是创建一个新的 std::string 对象。
函数名 Get:
函数名 Get 通常表示获取某个值。结合参数 key,可以推测该函数用于根据键(key)获取对应的值。
参数 const std::string& key:
参数类型是 const std::string&,表示函数接受一个对 std::string 对象的常量引用。
使用 const 引用可以避免拷贝字符串,同时保证函数内部不会修改传入的字符串。

实现了键值存储访问函数

在初始化中读取配置文件

工厂类添加宏

工厂类最终代码

main函数运行程序代码

posted @ 2025-04-28 19:38  plusu  阅读(97)  评论(0)    收藏  举报