【问题标题】:PHP dependency injection. Is my code here actually dependency injection container?PHP 依赖注入。我的代码实际上是依赖注入容器吗?
【发布时间】:2012-11-17 01:51:30
【问题描述】:

所以 - 我想将我的代码切换到依赖注入、依赖注入容器 (DIC) 范式。而且我一直在阅读它......在我看来,我一直在这样做......也许......

我在做什么 - 我正在为每个单独的项目创建命名空间类(容器?),我通常称之为 Api,然后我将实例化并从中获取配置的对象。实际例子:

<?php

/**
 * @namespace
 */
namespace SomeNamespace;

/**
 * api
 */
class api {
    public function __construct() {
        /*
         * Requiring all of the common files.
         */
        require_once( VENDORS_PATH . DIRECTORY_SEPARATOR . 'PHPMailer' . DIRECTORY_SEPARATOR . 'class.phpmailer.php' );
        require_once( VENDORS_PATH . DIRECTORY_SEPARATOR . 'PHPMailer' . DIRECTORY_SEPARATOR . 'class.smtp.php' );
    }

    private function getPostgresqlPreprocessor() {
        $spp = new \SomeNamespace\Utils\PostgresSearchPreProcessor();
        $settingsService = $this->getSettingsService();
        $settings = $settingsService->readSettings();
        $spp->setValidator( new \Auro\Validation\Validator() )
            ->setSettings( $settings )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_config.php' ) );
        return $spp;
    }

    public function getDalApi() {
        return new \DalServices\Api();
    }

    public function getFeedsService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $feedsService = new \SomeNamespace\Feeds();
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'feeds' );

        $feedsService
            ->setDalApi( new \DalServices\Api() )
            ->setSomeNamespaceApi( $this )
            ->setResponse( $fc->getResponse() )
            ->setPaginator( new \Auro\View\Paginator() )            
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_feeds_config.php' ) )
            ->setCams( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'webcams.php' ) )
            ->setRenderer( $renderer );
        return $feedsService;
    }

    public function getSettingsService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $settingsService = new \SomeNamespace\Settings;
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'settings' );

        $settingsService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_config.php' ) )
            ->setDalApi( new \DalServices\Api() )
            ->setResponse( $fc->getResponse() )
            ->setRenderer( $renderer );
        return $settingsService;
    }

    public function getUsersService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $usersService = new \SomeNamespace\Users();
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'users' );
        $usersService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_config.php' ) )
            ->setAvailableCurrencies( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'currencies.php' ) )
            ->setLanguages( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'languages.php' ) )           
            ->setPostgersqlPreprocessor( $this->getPostgresqlPreprocessor() )
            ->setResponse( $fc->getResponse() )
            ->setDalApi( new \DalServices\Api() )
            ->setRenderer( $renderer );
        return $usersService;
    }

    public function getVrcommentsService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $vrcommentsService = new \SomeNamespace\Vrcomments();
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'vrcomments' );
        $vrcommentsService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_config.php' ) )
            ->setPostgersqlPreprocessor( $this->getPostgresqlPreprocessor() )
            ->setPaginator( new \Auro\View\Paginator() )
            ->setResponse( $fc->getResponse() )
            ->setDalApi( new \DalServices\Api() )
            ->setRenderer( $renderer );
        return $vrcommentsService;
    }

    public function getVrentalsService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'vrentals' );
        $vrentalsService = new \SomeNamespace\Vrentals();
        $vrentalsService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_config.php' ) )
            ->setPostgersqlPreprocessor( $this->getPostgresqlPreprocessor() )
            ->setPaginator( new \Auro\View\Paginator() )
            ->setDalApi( new \DalServices\Api() )
            ->setRenderer( $renderer )
            ->setResponse( $fc->getResponse() )
            ->setMailConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_mail.php' ) )
            ->setMailer( new \PHPMailer( true ) );
        return $vrentalsService;
    }

    public function getNoAikService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'noaik' );
        $AikService = new \SomeNamespace\Noaik();
        $noAikService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_config.php' ) )
            ->setPostgersqlPreprocessor( $this->getPostgresqlPreprocessor() )
            ->setPaginator( new \Auro\View\Paginator() )
            ->setResponse( $fc->getResponse() )
            ->setDalApi( new \DalServices\Api() )
            ->setRenderer( $renderer )
            ->setMailConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_craig_mail.php' ) )
            ->setMailer( new \PHPMailer( true ) );
        return $noAikService;
    }

    public function getTotalauthService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $renderer = new \Auro\View\Renderer();
        $renderer->setBasePath( VIEWS_PATH . DIRECTORY_SEPARATOR . 'totalauth' );
        $totalauthService = new \SomeNamespace\Totalauth();
        $totalauthService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setRenderer( $renderer )
            ->setResponse( $fc->getResponse() )
            ->setDalApi( new \DalServices\Api() )
            ->setConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_totalauth_config.php' ) )
            ->setMailConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_totalauth_mail.php' ) )
            ->setMailer( new \PHPMailer( true ) );
        return $totalauthService;
    }

    public function getQuickmailService() {
        $fc = \Auro\Mvc\Front::getInstance();
        $quickmailService = new \SomeNamespace\Quickmail();
        $quickmailService
            ->setSomeNamespaceApi( $this )
            ->setValidator( new \Auro\Validation\Validator() )
            ->setResponse( $fc->getResponse() )
            ->setMailConfig( require( CONFIGS_PATH . DIRECTORY_SEPARATOR . 'models_quickmail_mail.php' ) )
            ->setMailer( new \PHPMailer( true ) );
        return $quickmailService;
    }
}

?>

有人可以确认这实际上是依赖注入容器吗?有什么可以改进的?

【问题讨论】:

  • 它看起来不像一个普通的 DI 容器。它现在只是一种服务定位器。请参阅github.com/ninject/ninject/wiki/Dependency-Injection-By-Hand 并点击最后的链接(它是 C#,但我相信你会明白的)。阅读第二页后,您会意识到 DI 容器通常是接口和特定实现之间的粘合层。所以你请求“给我一些接口IFoo的实现”并检索一个特定的对象。
  • 你也可以阅读mnapoli.github.com/PHP-DI它是一个PHP的依赖注入容器

标签: php dependency-injection


【解决方案1】:

对我来说,这看起来像是一组方法来实例化特定 API 所需的对象。我不知道我会考虑它本身的依赖注入。如果不看这个类是如何实际使用的,就很难说清楚。对我来说,依赖注入容器基本上包含有关如何实例化实现公共接口的各种类的元数据。

因此,可能会交互以实现依赖注入的一组示例类可能如下所示:

class db_dependency_provider {
    private static $class_map = array(
        'postgres' => 'postgres_abstraction_class',
        'mysql' => 'mysql_abstraction_class',
        'oracle' => 'oracle_abstraction_class'
    }

    public static function get_db_abstraction($type) {
        $class = self::$class_map[$type];
        return new $class();
    }
}

interface db_abstraction_interface {
    public function connect();
    public function query($query);
    // etc.
}

class mysql_db_abstraction implements db_abstraction_interface {
    // actual implementation
}

class postgres_db_abstraction implements db_abstraction_interface {
    // actual implementation
}

class some_class_that_needs_a_db {
    $db = null;
    public function __construct($db) {
        $this->db = $db;
    }
    // other methods
}

// use dependency injection container
$class = new some_class_that_needs_a_db(db_dependency_provider::get_db_abstraction('mysql'));

【讨论】:

    【解决方案2】:

    我在今年早些时候发布了一个类似问题的答案。我相信它应该解决您的担忧。 How can I use "Dependency Injection" in simple php functions, and should I bother?

    看起来您也可以从 PHP 自动加载中受益,Bart 项目以及 Composer 等其他开源项目也提供此功能。

    对于一般的依赖注入入门,您可以查看 Martin Fowler:http://martinfowler.com/articles/injection.html

    【讨论】:

      【解决方案3】:

      这并不是真正的依赖注入。

      依赖注入是指一段代码依赖于另一段代码。方法签名和预期返回值是已知的,但实现不是。你完全可以在 php 中做到这一点,但它与你所做的不同。

      这就是依赖注入的样子(完全人为的例子):

      <?php
      class PartnerAttachmentReader implements AttachmentReader{
        private $titleParsingStrategy; 
      
        private function __construct($titleParsingStrategy){
          $this->titleParsingStrategy = $titleParsingStrategy;
        }
      
        public static function getInstance($titleParsingStrategy){
          return new PartnerAttachmentReader($titleParsingStrategy);
        }
      
        public function getTitle($attachment){
          $this->titleParsingStrategy->parseTitle($attachment)
        }
      
        public function parseAttachment(){
           //Do Some Parsing
        }
      
      } 
      ?>
      

      $titleParsingStrategy 是什么?它是如何工作的?未知。重点是我不必在意。我可以在闲暇时替换其他实现,包括在测试时模拟的。

      DI 在处理磁盘或网络等易失性事物时也很有用。您可以在“依赖项”中分离磁盘和网络访问,并测试注入目标的完整性和正确性,而不必担心网络丢失或磁盘上的状态更改。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-15
        • 2018-09-13
        • 2013-04-30
        • 1970-01-01
        • 1970-01-01
        • 2016-11-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多