c++依赖注入+得墨忒耳定律+ logger/assert

c++ Dependency Injection + Law of Demeter + logger/assert

本文关键字:logger 定律 assert 依赖 注入 c++      更新时间:2023-10-16

我看过两个很棒的视频(这个和这个),关于依赖注入、德米特定律和全局状态(singleton被认为是全局的)。

我想我得到了基本的想法,但我已经有一些单例类在我的库。然而,如果我想要一个可测试的和"设计良好"或"耦合较少"的代码,我应该使用DI和LoD。这当然意味着单例模式(作为一种设计模式)是邪恶的,因为调用者不知道实现,任何对全局事物的依赖都是不好的,至少从测试的角度来看是这样。

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

让我们更具体一点。我有一个数学部分在我的库中,我有一个类Vector2。当为其中一个函数输入无效数据时,它应该能够"抛出断言"。或者应该能够将其记录为错误。或两者兼而有之。在此之前,我只是简单地使用Singleton<Logger>,所以我可以在任何地方访问它。

但是我同意,这些东西不应该被使用,而DI解决了这些问题。如。如果记录器还没有初始化怎么办?如果我想为测试使用一个虚拟记录器呢?等等......对于这些情况(如Logger和Assert类),您有什么建议?

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

考虑一个来自Unity引擎的例子。GameObject有一个从该对象获取组件的方法。如。如果我想手动转换我的对象,我没有选择调用"对象getter",像这样:
this.GetComponent<Transform>().SetPosition(...);

这是违背上帝的,不是吗?

这意味着我还必须处理特定于平台和低级别的代码。

使用依赖倒置(不仅仅是注入)。

对于这些情况(如Logger和Assert类),您建议使用什么?

DI要求你修改你的api,以允许你在使用它们的地方注入东西。为了避免不得不添加大量额外参数的情况(一个用于记录器,一个用于断言实现,或全局配置设置等),将它们组合在一起:

  • 创建一个运行时配置类
  • 向其添加服务(记录器服务、验证服务、配置服务等)
  • 传递运行时配置;

LoD还说我不应该使用对象的访问器(如getObjectA()->getObjectB()->doSomething())。相反,将它们作为参数传递给函数/构造函数。

这种类型的调用链有几个问题:

  • 它鼓励重复(如果你的代码中多次出现getObjectA()->getObjectB()->,那已经是一个维护问题)

  • 是不完整设计的糟糕替代品。LoD说,如果您需要从ObjectA的实例开始doSomething(),那么ObjectA应该有一个方法doSomething:

    void ObjectA::doSomething(ObjectA& a)
    {
        getObjectB()->doSomething();
    }
    

    (或相似的)。

    这为扩展"如何从ObjectA的实例开始完成doSomething"增加了一个自然的点,这有利于维护。

  • 它强加于所有需要doSomething的客户端代码,事实是它需要知道ObjectB的接口。这听起来很小,但问题是普遍存在的,并且当作为设计策略应用时,它会严重恶化(如果您的ObjectA不仅有ObjectB,而且还有ObjectC和ObjectD,这可能足以迫使您花费大量时间来维护依赖关系)。

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

这是违背上帝的,不是吗?

是的。代码可以分割如下:

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*