加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 如何模拟另一个类负责实例化的类?

发布时间:2020-12-15 09:22:30 所属栏目:大数据 来源:网络整理
导读:请考虑以下代码: type TFoo1 = class public procedure DoSomething1; end; TFoo2 = class private oFoo1 : TFoo1; public procedure DoSomething2; procedure DoSomething3; constructor Create; destructor Destroy; override; end;procedure TFoo1.DoSom
请考虑以下代码:

type
  TFoo1 = class
  public
    procedure DoSomething1;
  end;

  TFoo2 = class
  private
    oFoo1 : TFoo1;
  public
    procedure DoSomething2;
    procedure DoSomething3;
    constructor Create;
    destructor Destroy; override;
  end;


procedure TFoo1.DoSomething1;
begin
  ShowMessage('TFoo1');
end;

constructor TFoo2.Create;
begin
  oFoo1 := TFoo1.Create;
end;

destructor TFoo2.Destroy;
begin
  oFoo1.Free;
  inherited;
end;

procedure TFoo2.DoSomething2;
begin
  oFoo1.DoSomething1;
end;

procedure TFoo2.DoSomething3;
var
  oFoo1 : TFoo1;
begin
  oFoo1 := TFoo1.Create;
  try
    oFoo1.DoSomething1;
  finally
    oFoo1.Free;
  end;
end;

我正在为一个班级创建单元测试,我坚持下去.我的问题都是关于模拟对象和我应该使用的设计模式的最佳方法.我是单元测试的类不是由我创建的.

>在下面的示例中,我需要模拟Foo1,因为它向我在单元测试期间无法调用的Web服务发送请求.但是Foo1是由TFoo2构造函数创建的,我无法模仿它.在这种情况下我该怎么办?我应该修改TFoo2构造函数来接受像这样的Foo1对象吗?

constructor TFoo2.Create(aFoo1 : TFoo1)
begin
  oFoo1 := aFoo1;
end;

是否有一种设计模式表明我们需要传递一个类所依赖的所有对象,如上面的例子?
>方法TFoo2.DoSomething3创建Foo1对象然后释放它.我是否还应修改该代码以传递Foo1对象?

procedure TFoo2.DoSomething3(aFoo1 : TFoo1);
begin
  aFoo1 := aFoo1.DoSomething1;
end;

>是否有任何设计模式支持我提出的建议?如果是这样,我可以告诉我工作的公司的所有开发人员,我们需要遵循XXX模式,以便更容易进行单元测试.

解决方法

如果你不能模拟TFoo1的创建,那么你就不能模拟TFoo1.现在,TFoo2负责创建TFoo1的所有实例,但如果这不是TFoo2的主要目的,那么这确实会使单元测试变得困难.

正如您所建议的那样,一种解决方案是将TFoo2传递给它所需的任何TFoo1实例.这可能使您已调用TFoo2方法的所有当前代码复杂化.另一种方法,即对单元测试更友好,是为TFoo1提供工厂.工厂可以像函数指针一样简单,也可以是整个类.在Delphi中,元类也可以作为工厂.在构造时将工厂传递给TFoo2,每当TFoo2需要TFoo1实例时,它就可以调用工厂.

要减少对其余代码的更改,可以使工厂参数在TFoo2构造函数中具有默认值.然后您不必更改应用程序代码.只需更改单元测试代码即可提供非默认的工厂参数.

无论你做什么,你都需要让TFoo1.DoSomething1成为虚拟的,否则嘲笑将是徒劳的.

使用元类,您的代码可能如下所示:

type
  TFoo1 = class
    procedure DoSomethign1; virtual;
  end;

  TFoo1Class = class of TFoo1;

  TFoo2 = class
  private
    oFoo1 : TFoo1;
    FFoo1Factory: TFoo1Class;
  public
    constructor Create(AFoo1Factory: TFoo1Class = nil);
  end;

constructor TFoo2.Create;
begin
  inherited Create;
  FFoo1Factory := AFoo1Factory;
  if not Assigned(FFoo1Factory) then
    FFoo1Factory := TFoo1;

  oFoo1 := FFoo1Factory.Create;
end;

现在,您的单元测试代码可以提供TFoo1的模拟版本,并在创建TFoo2时传递它:

type
  TMockFoo1 = class(TFoo1)
    procedure DoSomething1; override;
  end;

procedure TMockFoo1.DoSomething1;
begin
  // TODO: Pretend to access Web service
end;

procedure TestFoo2;
var
  Foo2: TFoo2;
begin
  Foo2 := TFoo2.Create(TMockFoo1);
end;

许多元类的例子为基类提供了一个虚拟构造函数,但这并不是绝对必要的.如果需要虚拟调用构造函数,则只需要一个虚拟构造函数 – 如果后代构造函数需要使用基类尚未执行的构造函数参数.如果后代(TMockFoo1,在这种情况下)与其祖先完成所有相同的事情,那么构造函数不需要是虚拟的. (还要记住,AfterConstruction已经是虚拟的,所以这是让后代在不需要虚拟构造函数的情况下进行额外操作的另一种方法.)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读