c ++依赖注入+ Demeter定律+ logger / assert

时间:2016-10-07 09:42:41

标签: c++ oop dependency-injection law-of-demeter

我看过两个关于依赖注入,demeter定律和全局状态的好视频(thisthis)(Singletons被视为全局)。

我认为我有了基本的想法,但我的库里已经有了一些单例类。但是,如果我想要一个可测试的“精心设计”或“少耦合”的代码,我应该使用DI和LoD。这当然意味着Singletons(作为设计模式)是邪恶的,因为调用者现在不执行,并且对全局事物的任何依赖都是坏的,至少从测试的角度来看。

更具体地说,我正在构建一个简单的游戏引擎而不使用任何更大的第三方库。这意味着我必须使用特定于平台的代码和低级代码。

让我们更具体一点。我的库中有一个Math部分,我有一个Vector2类。当为其中一个函数输入无效数据时,它应该能够“抛出断言”。或者应该能够将其记录为错误。或两者。在此之前,我只使用Singleton<Logger>,因此我可以随处访问它。

但我同意,不应该使用这些东西,DI解决了这些问题。例如。如果记录器尚未初始化怎么办?如果我想要一个虚拟记录器进行测试怎么办?等等......你对这些情况(比如Logger和Assert类)推荐什么?

LoD也说我不应该使用对象的访问器(如getObjectA()->getObjectB()->doSomething())。而是将它们作为参数传递给函数/构造函数。它可以使一切变得更容易测试(和调试),但跳过这些功能可能会很痛苦。

考虑一下Unity引擎的一个例子。 GameObject有一种从该对象获取组件的方法。例如。如果我想手动转换我的对象,我别无选择地调用“对象getter”,如下所示:

this.GetComponent<Transform>().SetPosition(...);

这是针对LoD的,不是吗?

1 个答案:

答案 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*