我对依赖注入很新,我遇到的情况是我有一个基本上只是函数式编程的类 - 其中一个方法(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个对象似乎太过分了。
有更好的方法吗?
答案 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 standard和Symfony2's DIC,例如Laravel的DIC和PHP-DI,都符合这些标准,可以很容易地用于处理你的类加载应用
如上所述,PHP-DI是您可能想要查看的另一个DIC组件。