如何通过依赖注入重构存在代码异味与循环依赖的上帝类?
重构方案:解决SRP违反、循环依赖与DI框架限制问题
核心思路
通过抽象解耦循环依赖、延迟注入解决this传递限制、拆分职责修复SRP三个步骤完成重构,同时兼容无法修改的核心库God类。
1. 抽象解耦,打破循环依赖
现有代码中PrayersSvc直接依赖God*,导致与God形成循环依赖。我们提取PrayersSvc实际需要的行为到抽象接口,让PrayersSvc依赖抽象而非具体类。
代码实现
// 自定义抽象接口:仅暴露PrayersSvc需要的方法 class ISacrificeRequester { public: virtual ~ISacrificeRequester() = default; virtual void requestSacrifice() = 0; }; // 修改PrayersSvc,依赖抽象接口而非God类 class PrayersSvc { std::unique_ptr<ISacrificeRequester> sacrificeRequester; public: // 无参构造,适配DI框架初始化 PrayersSvc() = default; // Setter方法用于延迟注入依赖 void setSacrificeRequester(std::unique_ptr<ISacrificeRequester> requester) { sacrificeRequester = std::move(requester); } void handlePrayers(const Prayers& prayers) { // ... 原有业务逻辑 sacrificeRequester->requestSacrifice(); } };
2. 延迟注入,解决DI框架无法传递this的问题
DI框架禁止在构造函数中传递this,我们采用先构造实例、后注入依赖的方式,利用Setter方法完成最终依赖绑定。
代码实现
// 拆分Mustang的路由职责,新建GodHandlerRouter统一处理请求转发 class GodHandlerRouter : public God { std::unique_ptr<PrayersSvc> prayersSvc; // 可扩展:添加其他服务(如SacrificeSvc等) public: // 由DI框架注入PrayersSvc实例 explicit GodHandlerRouter(std::unique_ptr<PrayersSvc> prayersSvc) : prayersSvc(std::move(prayersSvc)) {} // 用于后续注入ISacrificeRequester依赖 void bindSacrificeRequester(ISacrificeRequester* requester) { prayersSvc->setSacrificeRequester(std::unique_ptr<ISacrificeRequester>(requester)); } // 转发handlePrayers请求到PrayersSvc void handlePrayers(const Prayers& prayers) override { God::handlePrayers(prayers); prayersSvc->handlePrayers(prayers); } // 其他handle方法同理,转发到对应服务 }; // Mustang仅负责重写God的核心方法,职责单一 class Mustang : public GodHandlerRouter, public ISacrificeRequester { public: // 继承父类构造函数,接收DI注入的服务 using GodHandlerRouter::GodHandlerRouter; // 仅实现requestSacrifice的自定义逻辑 void requestSacrifice() override { God::requestSacrifice(); // 调用核心库逻辑 // ... 原有自定义逻辑 } };
3. DI框架集成示例(以Boost.DI为例)
通过工厂函数完成实例创建与依赖绑定,适配DI框架的生命周期管理:
#include <boost/di.hpp> namespace di = boost::di; // 工厂函数:创建Mustang并完成依赖绑定 auto createMustang() { // 由DI框架注入PrayersSvc实例 auto prayersSvc = di::make_injector().create<std::unique_ptr<PrayersSvc>>(); auto mustang = std::make_unique<Mustang>(std::move(prayersSvc)); // 绑定Mustang自身作为ISacrificeRequester实现 mustang->bindSacrificeRequester(mustang.get()); return mustang; }
重构后优势
- 修复SRP违反:
Mustang仅负责重写God的核心方法,请求路由职责由GodHandlerRouter承担,各个业务逻辑由对应服务(如PrayersSvc)实现。 - 解除循环依赖:
PrayersSvc依赖抽象接口ISacrificeRequester,不再直接依赖God类。 - 适配DI框架:通过延迟注入避免构造函数传递
this,符合DI框架的约束。
内容的提问来源于stack exchange,提问作者haleyk
相关产品推荐
相关产品推荐

