从Delphi项目中删除单例实例

时间:2014-05-14 00:03:17

标签: delphi unit-testing mvp

我正在尝试制作可以进行单元测试的第一个项目。令人惊讶的是,我必须重新编写一些恶意编码风格的大脑。

这篇文章引起了我的注意Singletons are pathological liars

我并不想在这方面做出激进,但我习惯了一件神器,我不知道怎么能摆脱它呢

示例:

initialization
  ModelFactory.RegisterFactoryMethod('standard.contasmovimento',
    function(AParam: Variant) : TModel
    begin
      result := TModelContasMovimento.Create(AParam);
    end);

end.

ModelFactory是一个单例,在其单元上定义,是该单元的uses子句的一部分。

在我的MVP结构中,我在其自己的单元(1个1级单元)中定义了每个模型,视图和演示者。根据每个项目的需要,可以使用所有这些单元。因此,我将其用作零件目录,根据项目我将单元添加到项目中,它会自动注册并准备好从工厂使用。

为了解决单例问题,我想把它们移到框架类,所以我可以在一个点创建对象,然后可以使用依赖注入来传递框架对象。所有的工厂和其他环境都坐在那里:

TMyFramework = class
  FModelFactory: IModelFactory;
  FViewFactory: IViewFactory;
  FPresenterFactory: IPresenterFactory;

  property ModeFactory: IModelFactory read FModelFactory;
  ...

我的意思是以一种我可以在测试单元中嘲笑他们的方式移除单身人士。随着单身人士的到位,我无法轻易将其移除以进行测试。

但这会让我放松每个单元的自动初始化,我依靠它来添加单位。我不想手动创建可用类列表。

有没有办法解决这种情况?

4 个答案:

答案 0 :(得分:4)

这取决于您是否需要另一个类或工厂中的实例以在稍后实例化该类。在第一种情况下,您可以简单地将构造的实例作为依赖项传递。在另一种情况下,你通过工厂。在这两种情况下,你都使用依赖注入,而不是在其他单位中查找("不要寻找东西,但要求事情和#34;)。

当然,这需要一些依赖的连线代码("穷人的DI")或使用DI容器。

依赖注入确实是以干净的方式使事情可测试的唯一方法,因为你只是简单地将模拟传递给SUT。

答案 1 :(得分:4)

为了让单位测试在没有完全重写你所拥有的所有单身的情况下起步,我发现只需添加Free和重新Create单身人士的能力通常不仅仅是足以让你入门。

假设单例在初始化部分实例化(禁止那些。它们是任何单元测试的祸根。去单独的注册和初始化单元。),你可以简单地向接口部分添加两个过程。如果您不希望普通项目中的其他单位使用它们,请将它们置于条件定义之间:

{$IFDEF DUNIT}
procedure InstantiateMySingleton;
procedure FreeMySingleton;
{$ENDIF}

将您现在在初始化和完成部分中的代码移动到这些过程的实现中,并从初始化和完成中调用它们。

procedure InstantiateMySingleton;
begin
   // ...
end;

procedure FreeMySingleton;
begin
   // ...
end;

initialization
  InstantiateMySingleton;
finalization
  FreeMySingleton;

完成此操作后,您可以开始在单元测试中使用InstantiateMySingleton和FreeMySingleton。设置和拆解方法。

您要做的就是确保创建和销毁单例不会泄漏内存,并且每次都可以使用相同的功能结果实际重复。我发现有一件事有助于确保这是使用GUI运行器并运行整个测试套件两次(当然不退出GUI!)。如果测试在第一次运行时成功并在第二次运行时失败,则在单例的初始化或完成时遇到问题。

答案 2 :(得分:1)

一般来说,Misco正在研究(不)在其他对象中使用Singletons。它有点拉伸,但可以将delphi单元视为一个对象,所以问题是如何去除单元中的Singleton依赖。

最简单的解决方案,也是我一直使用的解决方案,是将将所有初始化代码移动到一个(或多个)单元中; s的唯一目的是为您的应用程序进行初始化。

对于您的测试用例,您现在可以自由地省略该单元并将(模拟)初始化为您想要的任何内容。

答案 3 :(得分:1)

如果我看得够,我相信我能找到一篇描述每一个设计模式的文章,无论是反模式还是魔鬼化身。例如,这里有一篇文章将服务定位器模式描述为反模式:

http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/

我看过将工厂模式描述为反模式的评论。

过去我经常在工厂里使用单身人士,我测试我在单元测试中“加载”这些工厂的适配器/插件/等等没有问题。通常,单例中的代码很简单,而不是我自己测试的东西。只需正常使用即可对代码进行足够的测试。我见过的所有依赖注入库都包含一个用于其全局容器的单例。

在可能的情况下,我喜欢用XML配置我的适配器。系统本身是完全模块化的,易于测试。

我不是说依赖注入很糟糕。离得很远。我喜欢。如果你是从头开始构建一个系统,我肯定会提倡它作为一个很好的框架来设计,Spring4D是一个很好的实现。我提出了一个不同的观点。我认为重写您的系统以尝试利用更新或不同的技术应该是您的最后一个停靠点。它让我想起Joel Spolsky关于Netscape重写Navigator(http://www.joelonsoftware.com/articles/fog0000000069.html)的文章。