DI - 如何重构具有大量依赖关系的构造函数

时间:2018-04-02 18:37:33

标签: php oop

我对依赖注入很新,我遇到的情况是我有一个基本上只是函数式编程的类 - 其中一个方法(createPayment)需要9个依赖项,并且其他方法(paypalApiEndpoint)只需要其中一个依赖项:

class Paypal
{
    private $restAPIClient;
    private $db;
    private $logger;
    private $paypalGateway;
    private $paymentGateway;
    private $ordersGateway;
    private $usersGateway;
    private $resourcesGateway;
    private $configGateway;

    public function __constructor($restAPIClient, $db, $logger, // ...6 more, each for the private vars) 
    {
        $this->restAPIClient = $restAPIClient;
        $this->db= $db;
        $this->logger= $logger;
        // ... 6 more
    }

    // this method uses all 9 dependencies from the constructor
    public function createPaypalPayment()
    {
        // insert purchase info into `payment` db table. Requires $paymentGateway and $db

        // make paypal API call. Requires $restAPIClient, $db, $paypalGateway, $logger

        // based on API call results, update `orders`, `users`, `payment`, `resources`, and `config` db tables. Requires $db, $logger, $paymentGateway, $ordersGateway, $usersGateway, $resourcesGateway, $configGateway
    }

    // this method only requires 1 of the dependencies, $paypalGateway
    public function paypalApiEndpoint()
    {
        // get a value from our db table `paypal`, and return it. Requires $paypalGateway
    }
}

在我的代码库中的许多地方,我需要使用paypalApiEndpoint方法。问题是,为了使用该方法,我必须首先创建9个对象(类Paypal的依赖项),然后我可以使用它们来创建Paypal对象,最后我可以使用简单的paypalApiEndpoint方法。需要为这个非常简单的方法创建10个对象似乎太过分了。

有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

// this method only requires 1 of the dependencies, $paypalGateway
public function paypalApiEndpoint()
{
    // get a value from our db table `paypal`, and return it. Requires $paypalGateway
}

如果唯一要求是paypalGateway,请将实施移至paypalGateway类或扩展名。

这并不意味着您必须从Paypal类中删除该方法:

public function paypalApiEndpoint()
{
    return $this->paypalGateway->paypalApiEndpoint();
}

此外,您的构造函数有很多参数。 我相信最好有一个带有所有依赖项的Builder类,使用setter,它们将作为参数注入:

https://jlordiales.me/2012/12/13/the-builder-pattern-in-practice/

或实现减少参数编号的相关数据的复合:

https://refactoring.guru/smells/data-clumps

考虑到Paypal构造函数有很多参数: https://refactoring.guru/smells/long-parameter-list

  

如果有多个不相关的数据元素,有时您可以合并   通过引入参数对象将它们转换为单个参数对象。

https://refactoring.guru/introduce-parameter-object

  •   

    通过合并单个类中的参数,您还可以移动   在那里处理这些数据的方法,释放另一个   此代码中的方法。

  •   

    (...)看看移动方法的一部分是否有任何意义   (有时甚至是整个方法)到参数对象类。如果   所以,使用Move Method或Extract Method。

这意味着,如果您只需要调用paypalApiEndpoint,则可以仅将paypalGateway类用于您的目的,其依赖性低于Paypal类。

答案 1 :(得分:1)

您不必在构造函数中注入所有依赖项。您可以为可选参数设置setter注入器。

public function setRestAPIClient($restAPIClient) {
    $this->restAPIClient = $restAPIClient;
};

在您的示例中,paypalApiEndpoint是一个很好的候选者,可以转换为类常量和/或静态方法,然后允许您通过以下方式使用它:

class PayPal {
    private static $paypalGateway = null;

    public static setPaypalGateway($paypalGateway) {
       self::paypalGateway = $paypalGateway;
    }

    public __construct(.... all params) {
       self::paypalGateway = $paypalGateway;
       $this->otherDependency = $otherDependency;
    }                
}

对于静态使用,你只需要调用一次静态setter,然后在脚本执行中尽可能多地使用它。

Paypal::setPaypalGateway($ppgateway);
$endpoint = Paypal::paypalApiEndpoint();

最后但并非最不重要的是,您可能希望研究DI容器组件的使用。

某些DIC现在具有自动装配功能,可以确定使用类型提示在呼叫时加载它们需要什么。

有关DI容器应如何工作的标准PSR standardSymfony2's DIC,例如Laravel的DIC和PHP-DI,都符合这些标准,可以很容易地用于处理你的类加载应用

如上所述,PHP-DI是您可能想要查看的另一个DIC组件。

相关问题