Это хорошая практика, чтобы DI-контейнер заменял глобальный объект $ registry? - PullRequest
9 голосов
/ 25 мая 2011

Я начал рефакторинг небольшого приложения, чтобы использовать маленький контейнер DI вместо $ registry :: getstuff ();вызовы в моих классах, я внедряю их в контейнер.

Это подняло 2 вопроса,

Q1 -> Я расширяю Класс Dimple DI и создаю контейнер с конкретными зависимостямидля каждого объекта, который будет нуждаться в DI.Затем я передаю объекту весь shebang и деконструирую его в конструкторе, присваивая объектам DI свойства класса создаваемого мной объекта.

Должен ли я отделять объект в вызове new object () ?Просто мне показалось, что так проще, но, видя, что я - команда из одного человека, сейчас я просто хочу подтвердить, что у меня есть правильная методология.

Q2 -> Я нахожу объект $ registry, который я передавал повсюду,быть ненужным, если я сделаю это на нескольких основных классах, это нормальный результат использования DI, больше нет реестра?У меня может быть один или два инжектированных в контейнер, но это выглядит так, как будто это все, что мне нужно, и даже те, которые могут быть легко устранены, поскольку DI имеет свойство share (), которое возвращает тот же экземпляр объекта, эффективно устраняя необходимостьдля одиноких.Это способ избавить приложение от необходимости регистрации / синглетонов, потому что если это так, то чертовски просто, как это.

1 Ответ

19 голосов
/ 08 июня 2011

Q2: Если вы обыскивали свой $registry объект .... тогда ваш Реестр на самом деле не был тем, что называется Реестр (как это описал Фаулер).

A Реестр - это более или менее глобальный объект («известный») с методами get / set.В PHP два общих прототипа для реализаций Registry были бы

как синглтон

class RegistryAsSingleton
{
    public static function getInstance (){
       //the singleton part
    }

    public function getStuff ()
    {
       //some stuff accessed thanks to the registry
    }
}

со статическими методами повсеместно

class RegistryAsStatic
{
    public static function getStuff()
    {
    }
}

Передача вашего реестра повсюду превращает его, в общем, просто в объект: контейнер, не имеющий большей цели, чем предоставление ссылок на другие объекты.

Ваш контейнер DI (используяПрыщ, как вы предложили в своем OP), является своего рода Registry : он хорошо известен и позволяет вам получать компоненты из любого места.

Так что да, мы можем сказать, что ваш DI-контейнерудалит требование и необходимость реестра, выполняя ту же самую функциональность.

НО (всегда есть но)

Реестр всегда виновен, пока не доказано невиновность (Мартин Фаулер)

Если вы используете DI-контейнер для замены Реестра , это, вероятно, неправильно.

Например:

//probably a Wrong usage of Registry
class NeedsRegistry
{
    public function asAParameter(Registry $pRegistry)
    {
       //Wrong dependency on registry where dependency is on Connection
       $ct = $pRegistry->getConnection();
    }

    public function asDirectAccess ()
    {
       //same mistake, more obvious as we can't use another component
       $ct = Registry::getInstance()->getConnection();
    }
}

//probably a wrong replacement for Registry using DI Container
class NeedsContainer
{
    public function asAParameter(Container $pRegistry)
    {
       //We are dependent to the container with no needs, 
       //this code should be dependent on Connection
       $ct = $pContainer->getConnection();
    }

    public function asDirectAccess ()
    {
       //should not be dependent on container
       $ct = Container::getInstance()->getConnection();
    }
}

Почему это плохо?Поскольку ваш код не менее зависим, чем прежде, он все еще зависит от компонента (реестра или контейнера), который не обеспечивает четкой цели (мы можем думать об интерфейсе здесь)

Шаблон реестра будетполезно в некоторых случаях, потому что это простой и довольно недорогой способ определения компонентов или данных (например, глобальная конфигурация).

Способ рефакторинга приведенного выше примера без использования DI путем удаления зависимости:

class WasNeedingARegistry
{
    public function asAParameter (Connection $pConnection)
    {
       $pConnection->doStuff();//The real dependency here, we don't care for 
       //a global registry
    }
}

//the client code would be like
$wasNeedingARegistry = new WasNeedingARegistry();
$wasNeedingARegistry->setConnection($connection);

Конечно, это может быть невозможно, если клиентский код не знает осоединение, которое, вероятно, является причиной, по которой вы, вероятно, вообще прекратили использовать Registry.

Теперь DI вступает в игру

Использование DI делает нашу жизнь лучше, потому что она будет обрабатывать зависимости ипозволяет нам получить доступ к зависимости в состоянии готовности к использованию.

Где-то в вашем коде вы будете настраивать свои компоненты:

$container['connection'] = function ($container) {
    return new Connection('configuration');
};
$container['neededARegistry'] = function ($container) {
    $neededARegistry = new NeededARegistry();
    $neededARegistry->setConnection($container['connection']);
    return $neededARegistry;
};

Теперь у вас есть все необходимое для рефакторингаВаш код:

// probably a better design pattern for using a Registry 
class NeededARegistry
{
    public function setConnection(Connection $pConnection)
    {
       $this->connection = $pConnection;
       return $this;
    }

    public function previouslyAsDirectAccess ()
    {
       $this->connection->doStuff();
    }
}

//and the client code just needs to know about the DI container
$container['neededARegistry']->previouslyAsDirectAccess();

Код "клиента" должен быть максимально изолированным.Клиент должен нести ответственность и внедрять свои собственные зависимости (с помощью set- методов).Клиент не должен нести ответственность за обработку зависимостей своих зависимостей.

class WrongClientCode
{
    private $connection;
    public function setConnection(Connection $pConnection)
    {
       $this->connection = $pConnection;
    }

    public function callService ()
    {
       //for the demo we use a factory here
       ServiceFactory::create('SomeId')
                       ->setConnection($this->connection)
                       ->call();
       //here, connection was propagated on the solely 
       // purpose of being passed to the Service
    }
}

class GoodClientCode
{
    private $service;
    public function setService(Service $pService)
    {
       //the only dependency is on Service, no more connection
       $this->service = $pService;
    }

    public function callService ()
    {
       $this->service->setConnection($this->connection)
                     ->call();
    }
}

Контейнер DI настроит GoodClientCode со службой, которая уже была правильно настроена с его подключением

Что касается аспекта Singleton, то да, он позволит вам избавиться от них,Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...