Silex:如何连接多个第 3 方库?

Silex: how to connect several 3rd-party libraries?

我正在尝试将第 3 方工具注入我在 Silex 中的自定义 class。然后我计划在某个时候用另一个库替换这个工具。为了符合 DI 原则,为了分离我的 class 和库,我将指定这些工具必须实现的接口。并在我的自定义 class 构造函数中键入提示接口。这将帮助我避免对 class 进行任何更改。像这样

class MyCustomClass 
{
    private $tool;

    public function __construct(MyCustomInterface $tool)
    {
        $this->tool = $tool;
    }
}

要注册那些第 3 方库,我必须为它们中的每一个创建服务提供商。那么我似乎需要某种适配器来使这些工具符合合同 MyCustomInterface 状态。事实证明,对于每个第 3 方工具,我必须管理 2 个额外的 classes(提供者和适配器)。

我的问题是:

据我所知,您的 DI 概念是正确的。如果您不想每次更改库时都重构整个代码(以及使用您的库的代码),那么适配器就是您要走的路:您定义一个特定的 API 接口,每个适配器必须实施。通过这种方式,您的代码可以安全地调用接口方法,而无需了解背后的实现。所以恕我直言,这是要走的路,你又来了(但我认为你在 Silex 上下文中的解决方案可以简化,是的,Silex 可以管理这个,任何其他现代框架也应该如此)。

为了简化您提出的解决方案,我不会为每个实施创建不同的提供程序。提供者将您的库与 Silex 绑定在一起,您只需告诉提供者必须使用哪个适配器(实现),您可以使用参数来做到这一点(请参阅代码示例末尾的注释)。所以我会选择这样的东西:

<?php

namespace Acme;

use Silex\Application;
use Silex\ServiceProviderInterface;
use Acme\MyCustomInterface; // This is your library interface

class MyCustomLibraryServiceProvider implements ServiceProviderInterface
{
    public function register(Application $app)
    {
        $app['some_dependency'] = $app->protect(function() use ($app) {
           // do whatever to create a new instance of this dependency
        });

        $app['another_dependency'] = $app->protect(function() use ($app) {
           // do whatever to create a new instance of this dependency
        });

        $app['my_service'] = $app->protect(function () use ($app) {
            // assuming your adapter has 2 dependencies.
            $myService = new $app['my_service_class']($app['some_dependency'], $app['another_dependency']);

            if (!$myService instanceof MyCustomInterface) {
                throw new \RuntimeException("The 'my_service_class' parameter must implement MyCustomInterface!");
            }

            return $myService;
        });
    }

    public function boot(Application $app)
    {
    }
}

然后当您创建 $app 实例时(您可以根据需要创建任意数量的适配器):

<?php
//...
use Acme\MyCustomLibraryServiceProvider;

//...
$app->register(new MyCustomLibraryServiceProvider(), [
    'my_service_class' => "Acme\MyCustomLibraryAdapter1"
]);

请注意,此解决方案假定每个适配器都具有相同的依赖关系。如果不是这种情况,您将需要为每个适配器创建另一个提供程序,但如果您不想这样做,请继续阅读:-)

如果您想进一步简化这一点,请考虑到您 根本 不需要创建提供程序。如果您的库没有依赖项或只有 1 或 2 个,您可以直接在创建应用程序实例的同一文件中创建服务(恕我直言,提供者仅在创建服务的代码很重时才有用):

<?php
//...
use Silex\Application;
use Acme\MyCustomLibraryAdapter1;

$app = new Application();
//...
$app['my_service'] = $app->protect(function() use ($app) {
  $dep1 = new WhatEver();

  return new MyCustomLibraryAdapter1($dep1);
});

此解决方案不太优雅但更简单(您需要更改为每个适配器创建服务的代码)。