依赖注入+完全虚拟与接口
Dependency injection + full virtual vs interfaces
我经常发现自己创建的接口只是在签名处使用以注入依赖项,以 class AIface
和 class AImpl : public AIface
结尾。而且我经常从不实现 class AIface
的任何其他子 class
与直接使用所有 public 虚拟方法的实现相比,这种方法有什么优势吗?
更长的解释:
假设我们有一个提供清洁服务的动物园。我们做 TDD,我们希望能够用一个假的 FeedingSvc 来测试 Zoo,所以我们进行依赖注入。
有什么区别:
class FeedingSvcIface{
virtual void performFeeding() = 0;
} ;
class RoboticFeedingSvc: public FeedingSvcIface{
void performFeeding();
};
Class Zoo{
Zoo(FeedingSvcIface&);
//...
};
对
class RoboticFeedingSvc{
virtual void performFeeding();
};
Class Zoo{
Zoo(RoboticFeedingSvc&);
//...
};
(如果以后需要,提取接口)
在测试方面,前者似乎更容易。
当我与 class 和 "crosses layers" 交谈时,我通常会很自然地添加接口,但有时它只是关于测试。
我知道将来我可能不得不实现其他类型的 FeedingSvcs
但如果我真的不需要,为什么今天要进行抽象?
我可能会拆分两个 classes 只是为了封装一些逻辑。
坚持最佳实践、设计模式或其他习语的好处在于,尽管您现在付出了一些额外的努力,但从长远来看您会收获更多 运行。
想象一下你在一个团队中工作的场景,有多个开发人员,一些有经验,一些没有。
您是 Zoo 机制的创建者,但您决定暂时根据 KISS 原则实现 Zoo,而不添加额外的抽象。你给自己设置了一个心理笔记(或者甚至是一个很好的小评论),说明 "If there shall be multiple distinct behaviors of the RoboticFeedingSvc there shall be Abstraction over the dependency injection !"。
现在,由于您的工作非常出色,您可以去度假,一些初级开发人员将留下来维护您的代码。
开发人员的任务之一是引入 ManualFeeding 选项和混合选项。您可以考虑多少种方法来做到这一点(无视任何编码原则)?
因为您,创建者,没有强制执行机制的增长方式,初级开发人员会查看您的评论,添加 "LoL u mad bro :) " 评论,然后选择以下之一:
- 创建一个由其他 FeedingSvcs 派生的基接口(你在这里很幸运)
- 使用策略模式创建对 RobotFeedingSvc 的依赖项注入(根据如何喂养一些仿函数来设置)
- 使 RobotFeedingSvc 成为 Feeder、Feed 和一些 Action 函数的复合体
- 使 RobotFeedingSvc 成为一个单例工厂(因为单例工厂很棒而且很花哨),在动物园内部以某种方式使用它来 return 适当的喂养技术(这里重要的是他使用了单例和工厂)
- 创建 Zoo 的模板化版本,它采用 RobotFeedingSvc 的模板化版本,根据给定的 FeedingPolicy 和 Feeder 进行部分特殊化(因为他只是碰到了模板,而模板应该随处使用)。
我想我们可以用更少的几行来总结这个故事:
首先努力正确地构建应用程序所需的抽象层,使其在功能方面具有可扩展性,这将有助于其他开发人员(包括未来的您)快速理解使用现有功能实现新功能的正确方法代码,而不仅仅是用一些疯狂的想法来破解它。
强制您的 Zoo Class 采用接口而不是具体 class 几乎等同于发表评论说新功能需要实现此接口。
允许将具体 class 作为参数传递可能会将注意力转移到如何更改具体 class 而不是在其之上实现某些东西。
另一个更技术性的原因如下:
他需要添加新功能,但不允许他更改 Zoo 实现。现在怎么办?
我经常发现自己创建的接口只是在签名处使用以注入依赖项,以 class AIface
和 class AImpl : public AIface
结尾。而且我经常从不实现 class AIface
与直接使用所有 public 虚拟方法的实现相比,这种方法有什么优势吗?
更长的解释:
假设我们有一个提供清洁服务的动物园。我们做 TDD,我们希望能够用一个假的 FeedingSvc 来测试 Zoo,所以我们进行依赖注入。
有什么区别:
class FeedingSvcIface{
virtual void performFeeding() = 0;
} ;
class RoboticFeedingSvc: public FeedingSvcIface{
void performFeeding();
};
Class Zoo{
Zoo(FeedingSvcIface&);
//...
};
对
class RoboticFeedingSvc{
virtual void performFeeding();
};
Class Zoo{
Zoo(RoboticFeedingSvc&);
//...
};
(如果以后需要,提取接口)
在测试方面,前者似乎更容易。
当我与 class 和 "crosses layers" 交谈时,我通常会很自然地添加接口,但有时它只是关于测试。
我知道将来我可能不得不实现其他类型的 FeedingSvcs
但如果我真的不需要,为什么今天要进行抽象?
我可能会拆分两个 classes 只是为了封装一些逻辑。
坚持最佳实践、设计模式或其他习语的好处在于,尽管您现在付出了一些额外的努力,但从长远来看您会收获更多 运行。
想象一下你在一个团队中工作的场景,有多个开发人员,一些有经验,一些没有。
您是 Zoo 机制的创建者,但您决定暂时根据 KISS 原则实现 Zoo,而不添加额外的抽象。你给自己设置了一个心理笔记(或者甚至是一个很好的小评论),说明 "If there shall be multiple distinct behaviors of the RoboticFeedingSvc there shall be Abstraction over the dependency injection !"。
现在,由于您的工作非常出色,您可以去度假,一些初级开发人员将留下来维护您的代码。
开发人员的任务之一是引入 ManualFeeding 选项和混合选项。您可以考虑多少种方法来做到这一点(无视任何编码原则)?
因为您,创建者,没有强制执行机制的增长方式,初级开发人员会查看您的评论,添加 "LoL u mad bro :) " 评论,然后选择以下之一:
- 创建一个由其他 FeedingSvcs 派生的基接口(你在这里很幸运)
- 使用策略模式创建对 RobotFeedingSvc 的依赖项注入(根据如何喂养一些仿函数来设置)
- 使 RobotFeedingSvc 成为 Feeder、Feed 和一些 Action 函数的复合体
- 使 RobotFeedingSvc 成为一个单例工厂(因为单例工厂很棒而且很花哨),在动物园内部以某种方式使用它来 return 适当的喂养技术(这里重要的是他使用了单例和工厂)
- 创建 Zoo 的模板化版本,它采用 RobotFeedingSvc 的模板化版本,根据给定的 FeedingPolicy 和 Feeder 进行部分特殊化(因为他只是碰到了模板,而模板应该随处使用)。
我想我们可以用更少的几行来总结这个故事:
首先努力正确地构建应用程序所需的抽象层,使其在功能方面具有可扩展性,这将有助于其他开发人员(包括未来的您)快速理解使用现有功能实现新功能的正确方法代码,而不仅仅是用一些疯狂的想法来破解它。
强制您的 Zoo Class 采用接口而不是具体 class 几乎等同于发表评论说新功能需要实现此接口。
允许将具体 class 作为参数传递可能会将注意力转移到如何更改具体 class 而不是在其之上实现某些东西。
另一个更技术性的原因如下:
他需要添加新功能,但不允许他更改 Zoo 实现。现在怎么办?