【问题标题】:Is this a valid usage of the Factory pattern? multiple dependencies这是工厂模式的有效用法吗?多重依赖
【发布时间】:2018-08-01 16:23:43
【问题描述】:

我想创建一组依赖项,而不是到处注入它们。工厂模式会支持这个想法吗?还是有另一种处理方式?

例如:

class PassportCheckFactory {
    protected $clientInstance;
    protected $responseInstance;

    public function buildDependancies() : bool
    {
        $this->clientInstance       = new PassportCheckSoapClient;
        $this->responseInstance    = new PassportCheckResponse;

        return true;
    }

    public function getResponseInstance()
    {
        return $this->responseInstance;
    }

    public function getClientInstance()
    {
        return $this->clientInstance;
    }
}

这会创建并保存我们将使用的类,因此我们不需要注入它们。

例如,我们可以这样做

$request = new WhateverRequestClass;
$factory = (new PassportCheckFactory)->buildDependancies();
$service = new PassportCheckService($request, $factory);
$response = $service->execute();

代替:

$request = new WhateverRequestClass;
$service = new PassportCheckService($request, new PassportCheckSoapClient, new PassportCheckResponse);
$response = $service->execute();   

【问题讨论】:

  • 我建议使用最后一个示例("instead of"),因为这是正确的依赖注入。它也将更容易测试(传递模拟对象等)。在您的其他示例中,您基本上已经将您的工厂类变成了服务定位器。您的工厂还将实例化所有类,无论您是否需要它们来完成该请求。
  • 我还建议您研究一些 IoC 容器,这些容器可以使您的代码更清晰、更易于管理,例如 Pimple、Illuminate\Container 或类似容器。这是一篇关于依赖注入和容器的 stackoverflow 帖子:stackoverflow.com/questions/18562752/…
  • 感谢@MagnusEriksson 如果我使用 IoC 容器,是否会将我的代码与该容器代码紧密耦合。假设我需要在遗留代码库中使用该功能,而要求不支持容器,我会被卡住吗?

标签: php unit-testing design-patterns phpunit tdd


【解决方案1】:

如果您想支持多个 CheckServices,您的方法是有意义的。 如果PassportCheckService 是唯一的,那么您示例中的工厂/服务定位器/专用容器只会增加开销。

$request = new WhateverRequestClass;
$service = new PassportCheckService($request, new PassportCheckSoapClient, new PassportCheckResponse);
$response = $service->execute();   

在可读性、可维护性和可测试性方面实际上是独立服务的最佳解决方案。

多重检查服务

但是,如果要支持多个服务,将服务的组合提取到自己的类中会带来好处。

class CheckServiceFactory
{
    public static function getService(Request $request, string $serviceType): CheckService
    {
        $type = ucfirst(strtolower($serviceType));

        $serviceClass  = $type . "CheckService";
        $clientClass   = $type . "CheckSoapClient";
        $responseClass = $type . "CheckResponse";

        return new $serviceClass($request, new $clientClass, new $responseClass);
    }
}

当然,类名的生成取决于您的命名方案。

调用特定服务如下所示:

$request = new WhateverRequestClass;
$service = CheckServiceFactory::getService($request, 'Passport');
$response = $service->execute();   

建议重构

除了上面的事情之外,我建议重构服务类本身,这样请求就会脱离构造函数。这改变了用法 到:

$request = new WhateverRequestClass;
$service = CheckServiceFactory::getService('Passport');
$response = $service->handle($request);   

或者在单一服务的情况下:

$request = new WhateverRequestClass;
$service = new PassportCheckService(new PassportCheckSoapClient, new PassportCheckResponse);
$response = $service->handle($request);

实际上看起来更直接。

【讨论】:

  • 好东西,我欠你一杯啤酒!你认为为了测试服务类,我可以简单地绕过工厂(因为它是静态的)并使用注入的模拟客户端创建服务?
  • 为了简短起见,我将工厂设为静态。最好保持非静态。但是您可以创建一个由同一工厂构建的“PassportTest”服务。
猜你喜欢
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多