我看过两个关于依赖注入,demeter定律和全局状态的好视频(this和this)(Singletons被视为全局)。
我认为我有了基本的想法,但我的库里已经有了一些单例类。但是,如果我想要一个可测试的“精心设计”或“少耦合”的代码,我应该使用DI和LoD。这当然意味着Singletons(作为设计模式)是邪恶的,因为调用者现在不执行,并且对全局事物的任何依赖都是坏的,至少从测试的角度来看。
更具体地说,我正在构建一个简单的游戏引擎而不使用任何更大的第三方库。这意味着我必须使用特定于平台的代码和低级代码。
让我们更具体一点。我的库中有一个Math部分,我有一个Vector2类。当为其中一个函数输入无效数据时,它应该能够“抛出断言”。或者应该能够将其记录为错误。或两者。在此之前,我只使用Singleton<Logger>
,因此我可以随处访问它。
但我同意,不应该使用这些东西,DI解决了这些问题。例如。如果记录器尚未初始化怎么办?如果我想要一个虚拟记录器进行测试怎么办?等等......你对这些情况(比如Logger和Assert类)推荐什么?
LoD也说我不应该使用对象的访问器(如getObjectA()->getObjectB()->doSomething()
)。而是将它们作为参数传递给函数/构造函数。它可以使一切变得更容易测试(和调试),但跳过这些功能可能会很痛苦。
考虑一下Unity引擎的一个例子。 GameObject有一种从该对象获取组件的方法。例如。如果我想手动转换我的对象,我别无选择地调用“对象getter”,如下所示:
this.GetComponent<Transform>().SetPosition(...);
这是针对LoD的,不是吗?
答案 0 :(得分:0)
这意味着我必须使用特定于平台的代码和低级代码。
使用dependency inversion(不仅仅是注射)。
对于这些情况(如Logger和Assert类),您有什么建议?
DI要求您更改API以允许您在使用它们的地方注入东西。为避免出现必须添加大量额外参数(一个用于记录器,一个用于断言实现或全局配置设置等)的情况,请将它们组合在一起:
LoD也说我不应该使用对象的访问器(比如getObjectA() - &gt; getObjectB() - &gt; doSomething())。而是将它们作为参数传递给函数/构造函数。
此类呼叫链接存在一些问题:
它鼓励重复(如果您的代码中有多次getObjectA()->getObjectB()->
,那已经是一个维护问题)
它是不完整设计的不良替代品。 LoD说如果你需要从doSomething()
的实例开始ObjectA
,那么ObjectA应该有一个方法doSomething:
void ObjectA::doSomething(ObjectA& a)
{
getObjectB()->doSomething();
}
(或类似)。
这增加了从ObjectA&#34;这对维护很有帮助。
它强加给需要doSomething
的所有客户端代码,它需要了解ObjectB的接口。这听起来很小,但问题很普遍,当作为设计策略应用时,它会很复杂(如果你的ObjectA不仅有一个ObjectB,还有一个ObjectC和一个ObjectD,这可能足以迫使你花很多钱时间只是维持依赖关系)。
this.GetComponent<Transform>().SetPosition(...);
这是针对LoD的,不是吗?
是。代码可以按如下方式拆分:
void SetPosition(Transform& t) { t.SetPosition(); }
客户代码:
SetPosition(this.GetComponent<Transform>());
这样,要在客户端代码中设置位置,您就不再关心Transform的界面了。您也不关心void SetPosition(Transform& t)
的实施,即有一个名为GetComponent
的API。
上述示例的替代LoD实现:
void YourObject::SetTransformPositions()
{
GetComponent<Transformation>.SetPosition();
}
... this
的类型为YourObject*
。