【C++编程】Inversion of the object-oriented solution

什么是Inversion of the object-oriented solution?

“Inversion of the object-oriented solution” 这个术语可能指的是在面向对象编程中,与传统的面向对象设计原则相反的一种设计方法。在传统的面向对象设计中,我们通常关注于创建对象,并通过这些对象来实现功能。而"inversion"可能意味着一种反转的思考方式,比如依赖注入(Dependency Injection),这是一种设计模式,它反转了控制权,让对象的创建和它们依赖的组件由外部提供,而不是由对象自己创建

中式英语解释:“Inversion of the object-oriented solution” 就是对象面向解决方案的反过来想。传统对象面向是造对象,用对象做事情。"Inversion"就是反过来想,比如依赖注入,是外面给对象东西,不是对象自己找东西。

因此,“Inversion of the object-oriented solution”可以看作是是一种编程思想,后来逐渐演化成为一种面向对象编程的原则,即DIP-依赖倒置原则(DIP)。

依赖倒置原则DIP

依赖倒置原则(DIP):依赖倒置原则是SOLID设计原则中的一个,它有两个基本要点:

高层模块不应该依赖低层模块。两者都应该依赖于抽象(接口或抽象类)。
抽象不应该依赖于细节(具体实现),细节应该依赖于抽象。
外部依赖:在依赖注入中,组件所需的依赖项通常由组件外部的代码提供。这样做的好处是降低了组件之间的耦合度,提高了组件的可重用性和可测试性。

架构设计

依赖倒置原则(DIP)的架构图通常包含以下几个关键组件:

高层模块(High-level Modules):这些是应用程序中的业务逻辑层或策略层,它们定义了应用程序的主要流程和决策点。

低层模块(Low-level Modules):这些模块通常包含具体的实现细节,如数据访问、第三方服务调用等。

抽象(Abstractions):这是一组接口或抽象类,定义了高层模块和低层模块需要实现或依赖的行为或数据结构。

具体实现(Implementations):这些是抽象的具体实现,由低层模块提供。

在依赖倒置原则中,高层模块不直接依赖于低层模块的具体实现,而是依赖于抽象。低层模块实现这些抽象,并可能依赖于其他低层模块的抽象。

定义接口
实现接口
实现接口
被依赖
提供实现
提供实现
高层模块
抽象
低层模块1
低层模块2

在这个图中:

A 代表高层模块,它定义了业务逻辑所需的接口。
B 是由高层模块定义的抽象,可以是接口或抽象类。
C 和 D 是低层模块,它们实现了由高层模块定义的抽象。
箭头表示依赖的方向,显示了高层模块依赖于抽象,而低层模块实现抽象并可能被高层模块使用。
这种架构允许高层模块保持独立于低层模块的具体实现,从而降低耦合度,提高系统的灵活性和可维护性。

依赖注入DI

依赖注入(Dependency Injection):这是一种软件设计模式,用于实现依赖倒置原则。它的核心思想是将组件需要的依赖项(如数据库连接、外部服务等)在外部创建并注入到组件中,而不是让组件自己创建或查找这些依赖。

注入方式:

构造函数注入:通过类的构造函数将依赖项传递给组件。这种方式的优点是确保了组件在使用前已经拥有了它所需要的所有依赖项。
方法注入:通过调用组件的某个方法来设置依赖项。这种方式比较灵活,但可能不如构造函数注入那样保证组件状态的完整性。
属性注入:直接将依赖项赋值给组件的属性。这种方式简单直观,但可能会在组件的生命周期中任意时刻被赋值,不如构造函数注入那样在组件创建时就确定。
实现DIP:通过依赖注入,组件不再直接依赖于具体的实现类,而是依赖于抽象的接口或抽象类。这样,当需要替换依赖项的具体实现时,不需要修改组件的代码,只需要提供一个新的实现并注入即可。

依赖注入(Dependency Injection,简称DI)是一种实现控制反转(Inversion of Control,IoC)的模式,它用于减少代码之间的耦合度并提高模块化。在依赖注入中,一个对象(通常称为“依赖项”)的创建和它所依赖的其他对象的提供被外部化,而不是由对象自己创建或查找。以下是依赖注入的几个关键方面:

目的:

降低模块间的耦合度。
提高代码的可测试性,因为依赖项可以被模拟或替换。
增加代码的可维护性,因为组件更容易独立变化。
注入方式:

构造函数注入:通过类的构造函数将依赖项传递给对象。这种方式强制要求对象在使用前必须拥有所有依赖项。
属性注入:依赖项被设置到对象的属性上。这种方式提供了灵活性,但可能不如构造函数注入安全。
方法注入:通过特定的方法将依赖项注入对象。这种方式不常用,但可以在运行时改变依赖项。
实现机制:

IoC 容器:一个框架或库,负责创建对象、管理它们的生命周期,并注入依赖项。例如,Spring Framework 中的 ApplicationContext。
手工注入:在某些情况下,开发者可能会手动创建对象并注入依赖项,尤其是在小型项目或框架不适用的情况下。
依赖项的生命周期:

单例:依赖项在整个应用程序中只有一个实例。
原型:每次请求都会创建一个新的依赖项实例。
请求/会话:依赖项的生命周期与HTTP请求或用户会话相关联。
依赖项的查找和绑定:

IoC 容器通常使用配置文件、注解或编程方式来定义依赖项的查找和绑定规则。
优点:

代码解耦:组件之间的依赖关系由外部配置管理,而不是硬编码在组件内部。
易于测试:可以轻松地替换依赖项进行单元测试。
提高生产力:开发者可以专注于业务逻辑,而不是对象的创建和管理。
缺点:

学习曲线:对于初学者来说,理解和使用依赖注入可能需要一些时间。
性能开销:IoC 容器可能会引入一些性能开销,尤其是在容器进行大量反射操作时。
过度使用:在某些情况下,过度使用依赖注入可能导致代码复杂性增加。
使用场景:

任何需要减少组件间耦合度的场景。
大型应用程序,其中组件的可维护性和可测试性至关重要。
需要快速迭代和频繁更改的应用程序。

举例

假设我们有一个 UserService 类,它依赖于一个 UserRepository 接口,而 UserRepository 有一个具体的实现 SqlUserRepository。

设计图
设计图展示了类和接口的结构,以及它们之间的依赖关系。

classDiagram
    class UserService {
        +void registerUser(User user)
        +User findUserById(int id)
    }
    interface UserRepository {
        +void saveUser(User user)
        +User getUserById(int id)
    }
    class SqlUserRepository {
        +void saveUser(User user)
        +User getUserById(int id)
    }
    
    UserService --|> UserRepository : Uses
    UserRepository <|-- SqlUserRepository : Implements

交互流程图

Dev Container UserSvc Repo Dev Container UserSvc Repo Configure DI Create instance Request UserRepository Create SqlUserRepository instance Inject Repo saveUser(User) User saved getUserById(int) return User Dev Container UserSvc Repo Dev Container UserSvc Repo

类图中,UserService 依赖于 UserRepository 接口,而 SqlUserRepository 实现了 UserRepository 接口。

在序列图中,展示了以下步骤:

开发者配置依赖注入。
IoC 容器创建 UserService 实例。
UserService 请求 UserRepository。
IoC 容器创建 SqlUserRepository 实例并注入到 UserService。
UserService 使用注入的 UserRepository 执行保存和检索用户的操作。

这些图提供了依赖注入概念的视觉表示,展示了类和接口的结构以及对象之间的交互过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值