C++设计模式:面向对象的八大设计原则之一

依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个重要原则,它强调高层次的代码不应该依赖低层次的代码,抽象的代码不应该依赖具体的代码,而是低层次代码应该依赖高层次的抽象。下面结合 C++ 代码详细讲解这一原则。

未遵循依赖倒置原则的示例

假设我们要实现一个简单的日志系统,包含一个 FileLogger类用于将日志信息写入文件,还有一个 Application类,它依赖于 FileLogger来记录日志。

cpp 复制代码
#include <iostream>

#include <fstream>

#include <string>



// 低层次模块:文件日志记录器

class FileLogger {

public:

    void log(const std::string& message) {

        std::ofstream file("log.txt", std::ios::app);

        if (file.is_open()) {

            file << message << std::endl;

            file.close();

        }

    }

};



// 高层次模块:应用程序

class Application {

private:

    FileLogger logger;

public:

    void doSomething() {

        logger.log("Doing something...");

    }

};



int main() {

    Application app;

    app.doSomething();

    return 0;

}

在这个例子中,Application是高层次模块,FileLogger是低层次模块。Application直接依赖于 FileLogger,这违反了依赖倒置原则。如果我们想要更换日志记录方式,比如改为将日志记录到控制台,就需要修改 Application类的代码。

遵循依赖倒置原则的示例

为了遵循依赖倒置原则,我们引入一个抽象的日志记录接口 ILogger,让 Application依赖于这个抽象接口,而 FileLogger和 ConsoleLogger都实现这个接口。

cpp 复制代码
#include <iostream>

#include <fstream>

#include <string>



// 抽象接口:日志记录器

class ILogger {

public:

    virtual void log(const std::string& message) = 0;

    virtual ~ILogger() {}

};



// 低层次模块:文件日志记录器

class FileLogger : public ILogger {

public:

    void log(const std::string& message) override {

        std::ofstream file("log.txt", std::ios::app);

        if (file.is_open()) {

            file << message << std::endl;

            file.close();

        }

    }

};



// 低层次模块:控制台日志记录器

class ConsoleLogger : public ILogger {

public:

    void log(const std::string& message) override {

        std::cout << message << std::endl;

    }

};



// 高层次模块:应用程序

class Application {

private:

    ILogger* logger;

public:

    Application(ILogger* logger) : logger(logger) {}

    void doSomething() {

        logger->log("Doing something...");

    }

};



int main() {

    // 使用文件日志记录器

    FileLogger fileLogger;

    Application app1(&fileLogger);

    app1.doSomething();



    // 使用控制台日志记录器

    ConsoleLogger consoleLogger;

    Application app2(&consoleLogger);

    app2.doSomething();



    return 0;

}    

代码解释

抽象接口 ILogger:定义了一个纯虚函数 log,任何具体的日志记录器都必须实现这个函数。这是一个抽象的代码,高层次的 Application 类依赖于这个抽象接口,而不是具体的日志记录器实现。

具体实现类 FileLogger 和 ConsoleLogger:它们都实现了 ILogger 接口,因此可以作为 ILogger 类型的对象传递给 Application 类。

高层次模块 Application:通过构造函数接收一个 ILogger 指针,从而依赖于抽象接口。这样,我们可以在运行时动态地选择不同的日志记录器,而不需要修改 Application 类的代码。

优点

可维护性:当需要更换日志记录方式时,只需要创建一个新的实现 ILogger 接口的类,而不需要修改 Application 类的代码。

可扩展性:可以轻松地添加新的日志记录器,如数据库日志记录器、网络日志记录器等,而不会影响现有的代码。

可测试性:可以使用模拟对象(Mock Object)来实现 ILogger 接口,从而方便地对 Application 类进行单元测试。

相关推荐
卡提西亚9 分钟前
C++笔记-21-运算符重载
c++·笔记
国服第二切图仔14 分钟前
Rust开发实战之简单游戏开发(piston游戏引擎)
开发语言·rust·游戏引擎
ii_best21 分钟前
安卓/IOS工具开发基础教程:按键精灵一个简单的文字识别游戏验证
android·开发语言·游戏·ios·编辑器
草莓熊Lotso25 分钟前
C++ 继承特殊场景解析:友元、静态成员与菱形继承的底层逻辑
服务器·开发语言·c++·人工智能·经验分享·笔记·1024程序员节
诗句藏于尽头28 分钟前
电脑使用软件控制本机屏和外接屏失效问题及解决
开发语言
不是株35 分钟前
JavaWeb(后端进阶)
java·开发语言·后端
利刃大大44 分钟前
【动态规划:01背包】01背包详解 && 模板题 && 优化
c++·算法·动态规划·力扣·背包问题
9ilk1 小时前
【基于one-loop-per-thread的高并发服务器】--- 前置技术
运维·服务器·c++·笔记·后端·中间件
千里镜宵烛2 小时前
深入 Lua 环境机制:全局变量的 “容器” 与 “隔离术”
开发语言·junit·lua
苏比的博客3 小时前
Windows MFC添加类,变量,类导向
c++·windows·mfc