我已经看了两个很棒的视频(this和this),它们有关依赖项注入(inject),得墨meter耳定律和全局状态(单子(monad)被视为全局)。
我想我已经有了基本的想法,但是我的图书馆中已经有一些单例类。但是,如果我想要一个可测试且“设计良好”或“耦合较少”的代码,则应使用DI和LoD。当然,这意味着Singletons(作为一种设计模式)是邪恶的,因为调用者现在没有实现,并且对全局事物的任何依赖都是不好的,至少从测试的 Angular 来看。
更具体地说,我正在构建一个简单的游戏引擎,而无需使用任何更大的第三方库。这意味着我还必须使用特定于平台的底层代码。
让我们更具体一点。我的图书馆中有一个Math部分,其中有Vector2类。当为其功能之一输入了无效数据时,它应该能够“抛出断言”。或者应该能够将其记录为错误。或两者。在此之前,我只使用了Singleton<Logger>
,因此我可以在任何地方访问它。
但是我同意,这些东西不应该使用,DI解决了这些问题。例如。如果记录器尚未初始化怎么办?如果我想要虚拟记录器进行测试怎么办?依此类推...对于这些情况,您有什么建议(例如Logger和Assert类)?
此外,LoD表示我不应该将访问器用于对象(例如getObjectA()->getObjectB()->doSomething()
)。而是将它们作为参数传递给函数/构造函数。可以使所有内容的测试(和调试)更加容易,但是跳过这些功能可能会很痛苦。
考虑来自Unity引擎的示例。 GameObject具有从该对象获取组件的方法。例如。如果要手动转换对象,则别无选择,请调用“对象 getter ”,如下所示:
this.GetComponent<Transform>().SetPosition(...);
这违背了LoD,不是吗?
最佳答案
使用dependency inversion(不仅用于注入(inject))。
DI要求您更改您的API,以允许您在使用它们的地方注入(inject)事物。为避免必须添加许多额外参数(一个用于记录器,一个用于断言实现或全局配置设置等)的情况,请将它们组合在一起:
这种类型的 call 链接存在一些问题:
getObjectA()->getObjectB()->
,那已经是维护问题)doSomething()
实例开始的ObjectA
,则ObjectA应该具有doSomething方法:void ObjectA::doSomething(ObjectA& a)
{
getObjectB()->doSomething();
}
(或类似)。
这为扩展有益于维护的“如何从ObjectA实例开始完成doSomething”添加了自然点。
doSomething
的客户端代码强加到void SetPosition(Transform& t)
,这是它需要了解ObjectB接口(interface)的事实。这听上去很小,但问题无处不在,并且作为设计策略应用时,它的组合效果很差(如果您的ObjectA不仅具有ObjectB,还具有ObjectC和ObjectD,这可能足以迫使您花很多钱时间只是保持依赖关系)。 是。可以按以下方式拆分代码:
void SetPosition(Transform& t) { t.SetPosition(); }
客户代码:
SetPosition(this.GetComponent<Transform>());
这样,就可以在客户端代码中设置位置,您不再关心Transform的界面。您也不必关心
GetComponent
的实现,因为有一个名为this
的API。以上示例的替代LoD实现:
void YourObject::SetTransformPositions()
{
GetComponent<Transformation>.SetPosition();
}
...此处
YourObject*
的类型为ojit_code。关于c++ - c++依赖注入(inject)+ Demeter定律+ logger/assert,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39914190/