php – 如何在完整的OO应用程序中处理依赖注入
作者:互联网
我在课堂设计上陷入两难境地.
我正在尽力尊重SOLID原则,但我不知道如何处理依赖注入.
这是我的困境:
>我认为在类中实例化对象以避免引入依赖是一种不好的做法.那么在完整对象应用程序中应该在哪里创建依赖项呢?在一个只负责依赖实例化的特殊对象中?如果是,该对象的名称是什么以及如何定义它?这就是我们所说的“控制器”吗?
>这个“控制器”,什么是单元测试的正确方法呢?我们应该进行单元测试吗?
>在完整的POO应用程序中,如何避免在类之间传递我们的对象(通常是相同的)?例如,一个DB对象,Log,……这样,我们冒险让构造函数有很多参数,不是吗?
为了说明我的困境,我试图创建一个用例.
我想创建一个脚本(我在下面部分实现),生成一个文件并打印另一个文件:
<?php
/**
* Generate a file, add it to the queue and print the next one
*/
class Script
public function run() {
#Generate file
$fileComputor = new FileComputer(...);
$file = $fileComputor->compute();
#Instantiate dependencies for printing
$db = new Db(new PdoAdapter());
$printerDriver = new Driver(new HttpRequest(new CurlAdapter()));
$log = new Log($db);
$queue = new Queue($db);
$statsUpdater = new StatsUpdater($db);
$monitor = new Monitor(new StatsdDriver(new SocketAdapter()));
#Add generated file to the queue
$queueModel->push($file);
#Print the next file on queue if existing
$printer = new Printer($printerDriver, $log, $queue, $monitor, $statsUpdater);
$printer->print();
}
}
class Printer {
protected $_driver;
protected $_log;
protected $_queue;
protected $_monitor;
protected $_statsUpdater;
/**
* $driver : Driver used to send documents to the printer
* $log : Log actions in database
* $queue : Handle the print queue
* $monitor : Send metrics to Statsd (to feed the graphit GUI)
* $statsdUpdater : Consolidate the statistics database
*/
public function __construct($driver, $log, $queue, $monitor, $statsUpdater) {
$this->_driver = $driver;
$this->_log = $log;
$this->_queue = $queue;
$this->_monitor = $monitor
$this->_statsUpdater = $statsUpdater;
}
public function print() {
if ($this->_queue->hasNext()) {
$file = $this->_queue->getNext();
$this->_driver->print($file);
$this->_log->log('File has been printed');
$this->_monitor->sendCount(1);
$this->_statsUpdater->increment();
}
}
}
?>
您对此实施有何看法?
我们想要插入Printer类的每个功能都将导致一个新的依赖项传递给构造函数(例如,我们想要生成一个syslog,来测量打印机处理的时间等).
在不久的将来,我们将在构造函数调用中有10到15个参数.
解决方法:
So where should our dependencies be created in a full object application? In a special object which is only responsible for dependency instantiations?
你有2个选择:
>您可以在应用程序的根目录下自行创建所有对象,例如在前端控制器(index.php)中.如果你的应用程序有点大,那很快就会成为一个地狱.
>您使用依赖注入容器.该对象将负责创建对象(并在构造函数中将它们的依赖项注入它们).同样在这里:您必须仅在应用程序的根目录中使用/调用容器,例如在前端控制器(index.php)中.
If yes, what is the name of this object and how to define it? Is it what we call “controller”?
这就是容器.举个例子,这里是PHP-DI – Understanding DI.
您可以在控制器上使用依赖注入(我建议这样做):您可以在控制器的构造函数中获得依赖项(就像在任何服务中一样).有些框架虽然很难(例如Symfony).
This “controller”, what is the right way to unit test it? Should we unit test it?
不完全是.某些容器允许您配置“工厂”以生成一些对象.
例如,如果创建DBConnection对象很复杂,则可以编写工厂类,该工厂类具有创建DBConnection对象的方法.所以你可以测试工厂类.但我不认为这是必要的.
In a full POO application, how to avoid passing our objects (often the same) between classes?
你永远不应该传递实例,因为你永远不应该调用构造函数:所有对象都是由容器构造的.
所以它变得非常简单:你通过在构造函数中使用依赖来编写每个类,就是这样.您不关心依赖关系以及这些依赖关系需要什么.
For example, a DB object, Log, … In this way, we take the risk to have constructors with many parameters, don’t we?
是.你说你希望在构造函数中有15-20个参数:这根本不好.
您通常应该尝试最多2-3个参数.有许多意味着你的班级有太多的责任,它对很多事情都有.
您可以尝试将类的代码拆分为几个较小/更具针对性的类,或者使用事件.
如果我们举个例子,你的打印机可能会像:
public function print($file) {
$this->driver->print($file);
$this->log->log('File has been printed');
$this->monitor->sendCount(1);
$this->statsUpdater->increment();
}
它更有意义:打印机打印文件.
这是一个较少的依赖(队列).然后你可以有一个PrintQueueProcessor来监视队列,获取下一个文件并调用打印机进行打印.
打印机执行一项任务(打印文件),队列处理器执行一项作业(排队文件以打印它们).
标签:php,oop,dependency-injection,solid-principles 来源: https://codeday.me/bug/20190825/1716919.html