Moose类的依赖注入

zou*_*oul 9 perl dependency-injection moose

我有一个需要发送类型请求的Moose类Foo::Request.我需要从外部访问这个依赖项,以便我可以轻松地在测试中交换请求实现.我想出了以下属性:

has request_builder => (
    is => 'rw',
    isa => 'CodeRef',
    default => sub {
        sub { Foo::Request->new(@_) }
    }
);
Run Code Online (Sandbox Code Playgroud)

然后在代码中:

my $self = shift;
my $request = $self->request_builder->(path => …);
Run Code Online (Sandbox Code Playgroud)

在测试中:

my $tested_class = …;
my $request = Test::MockObject->new;
$request->mock(…);
$tested_class->request_builder(sub { $request });
Run Code Online (Sandbox Code Playgroud)

是否有更简单/更惯用的解决方案?

Eri*_*ikR 1

考虑这种方法:

在您的 Moose 类中定义一个名为 的“抽象”方法make_request。然后定义两个角色来实现make_request——一个角色调用Foo::Request->new,另一个角色调用Test::MockObject->new

例子:

你的主要课程和两个角色:

package MainMooseClass;
use Moose;
...
# Note: this class requires a role that
# provides an implementation of 'make_request'


package MakeRequestWithFoo;
use Moose::Role;
use Foo::Request; # or require it
sub make_request { Foo::Request->new(...) }

package MakeRequestWithMock;
use Moose::Role;
use Test::MockRequest;  # or require it
sub make_request { Test::MockRequest->new(...) }
Run Code Online (Sandbox Code Playgroud)

如果您想测试您的主类,请将其与“MakeRequestWithMock”角色混合:

package TestVersionOfMainMooseClass;
use Moose;
extends 'MainMooseClass';
with 'MakeRequestWithMock';

package main;
my $test_object = TestVersionOfMainMooseClass->new(...);
Run Code Online (Sandbox Code Playgroud)

如果您想将其与“make_request”的 Foo 实现一起使用,请将其与“MakeRequestWithFoo”角色混合。

一些优点:

您将只加载您需要的模块。例如,该类TestVersionOfMainMooseClass不会加载模块Foo::Request

make_request您可以添加与新类的实例成员的实现相关/所需的数据。例如,您使用 CODEREF 的原始方法可以通过以下角色实现:

package MakeRequestWithCodeRef;
use Moose::Role;
has request_builder => (
  is => 'rw',
  isa => 'CodeRef',
  required => 1,
);
sub make_request { my $self = shift; $self->request_builder->(@_) };
Run Code Online (Sandbox Code Playgroud)

要使用此类,您需要为 提供初始化程序request_builder,例如:

package Example;
use Moose;
extends 'MainMooseClass';
with 'MakeRequestWithCodeRef';

package main;
my $object = Example->new(request_builder => sub { ... });
Run Code Online (Sandbox Code Playgroud)

作为最后的考虑,您编写的角色可能可用于其他类。