编程语言
首页 > 编程语言> > php – 如何在完整的OO应用程序中处理依赖注入

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