Это вменяемая реализация инжектора конструктора? - PullRequest
1 голос
/ 14 декабря 2009

Исходя из моего вопроса о сервисных локаторах Я решил вместо этого использовать инжектор конструктора. Пожалуйста, рассмотрите следующий код:

<?php

    interface IAppServiceRegistry {
        public function getDb();
        public function getLogger();
    }

    interface IFooServiceRegistry extends IAppServiceRegistry {
        public function getFooBarBazModel();
    }

    class AppServiceRegistry
        implements IAppServiceRegistry, IFooServiceRegistry
    {
        private $logger;
        private $db;
        private $fooBarBazModel;

        public function getDb() {
            // return db (instantiate if first call)
        }

        public function getLogger() {
            // return logger (instantiate if first call)
        }

        public function getFooBarBazModel() {
            if (!isset($this->fooBarBazModel)) {
                $this->fooBarBazModel = new FooBarBazModel( $this->getDb() );
            }
            return $this->fooBarBazModel;
        }
    }

    // Example client classes:

    /**
     * Depends on db, logger and foomodel.
     */
    class Foo {
        private $db;
        private $logger;
        private $fooModel;

        public function __construct(IFooServiceRegistry $services) {
            $this->db = $services->getDb();
            $this->logger = $services->getLogger();
            $this->fooModel = $services->getFooModel();
        }
    }

    /**
     * Depends on only db and logger.
     */
    class BarBaz {
        private $db;
        private $logger;

        public function __construct(IAppServiceRegistry $services) {
            $this->db = $services->getDb();
            $this->logger = $services->getLogger();
        }
    }

Затем я бы добавил новые методы фабрики сервисов в реестр по мере развития приложения, создавая сегрегированные интерфейсы там, где это логически целесообразно.

Является ли этот подход вменяемым?

Ответы [ 3 ]

4 голосов
/ 14 декабря 2009

Хотя я обычно не читаю php, я думаю, что понимаю большинство из них. Технически это выглядит хорошо, но тогда вы пишете

Я бы добавил новые методы фабрики сервисов в реестр по мере развития приложения

Это может повредить идее слабой связи, потому что вместо универсального сервисного локатора у вас теперь есть специализированный сервисный локатор.

Внедрение такого реестра serivce в каждый класс работает против Принципа единой ответственности (SRP), потому что, когда у класса есть доступ к реестру, слишком легко запросить еще одну зависимость, чем предполагалось изначально, и в итоге вы получите Объект Бога .

Было бы лучше внедрить необходимые зависимости непосредственно в классы, которые в них нуждаются. Таким образом, ваш конструктор класса Foo должен принимать db, logger и fooModel, а класс BarBaz должен принимать только параметр db и logger.

Тогда может возникнуть следующий вопрос: что, если мне нужно много разных зависимостей для выполнения работы? Это потребует действительно уродливого конструктора с большим количеством параметров, и это идет вразрез с другими хорошо известными практиками ОО.

Верно, но если вам нужно много зависимостей, вы, вероятно, нарушаете SRP и должны попытаться разделить ваш дизайн на более мелкие объекты:)

1 голос
/ 15 декабря 2009

В последнее время я сталкиваюсь с такой проблемой. Service Locator vs Depedency Injection.

Я согласен с Марком в том, что нужно вводить отдельные мелкозернистые объекты в конструктор по мере необходимости. Единственный недостаток, как подчеркнул Марк, состоит в том, что при построении графа сложного объекта вам неизбежно придется начинать где-то . Это означает, что ваши высокоуровневые объекты будут иметь лотов сервисов (объектов), внедренных в них.

Самый простой способ - использовать что-то для выполнения тяжелой работы за вас. Ярким примером этого является Google Guice, который мне кажется очень хорошим способом решения проблем. К сожалению, это написано для Java! Есть версии о PHP; Я не уверен, что кто-то из них вполне соответствует Guice на данный момент.

Я написал пост на эту тему , в котором более подробно; что вы можете найти интересным. Он включает в себя простую реализацию инфраструктуры внедрения зависимостей .

Суть в том, что если у вас есть класс Foo с рядом требований, вы можете создать свой класс следующим образом:

/**
 * Depends on db, logger and foomodel.
 */
class Foo
{
    private $db;
    private $logger;
    private $fooModel;

    /**
     * (other documentation here)
     * @inject
     */
    public function __construct(IDbService $db, ILoggerService $logger, $iModelService $model)
    {
       // do something
    }
}

Если вам нужен новый объект Foo , вы просто запрашиваете структуру внедрения зависимостей , создающую вам один :

$foo = $serviceInjector->getInstance('Foo');

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

Позже, когда вы обнаружите, что вам нужен объект IBarService , вы можете просто добавить его в Construtor, не изменяя никакого другого кода!

1 голос
/ 14 декабря 2009

Эта реализация практически совпадает с локатором службы, который вы ранее нам показали.

Хороший вопрос, который нужно задать, заключается в том, знаете ли вы, что, взглянув на класс ваших объектов, вам нужно знать все об объектах, чтобы выполнить свою работу. В вашем случае вы все еще не знаете.

Если Foo нужны БД, регистратор и модель, вы должны это выяснить, запросив их в конструкторе.

Вот хорошее прочтение по этому вопросу:

http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/

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