IoC на PHP

    Inversion of Control (IoC) контейнеры — это удобный способ организации внедрения зависимости получивший широкое применение в мире Java.

    Данная библиотека позволяет использовать IoC контейнеры в PHP.



    Для примера опишем несколько интерфейсов:

    interface First {
        public function first();
    }
    
    interface Second {
        public function second();
    }
    
    interface Third {
        public function third();
    }
    


    И их реализаций:

    class Foo implements First {
        public function first() { /* ... */ }
    }
    
    class Boo implements Second {
        public function __construct(First $first) { /* ... */ }
    
        public function second() { /* ... */ }
    }
    
    class Woo implements Third {
        public function __construct(First $first, Second $first) { /* ... */ }
    
        public function third() { /* ... */ }
    }
    


    Теперь воспользуемся IoC контейнером для описания зависимостей:

    $ioc = IoC\Container::getInstance();
    
    $ioc->register('Foo');
    $ioc->register('Boo');
    $ioc->register('Woo');
    


    Зависимости назначаются автоматически. Класс Foo реализует интерфейс First: в IoC контейнере First ссылается на Foo.

    Опишем класс-менеджер зависящий от этих интерфейсов:

    class Manager
    {
        use IoC\Creatable;
    
        public function __construct(First $first,
                                    Second $second,
                                    Third $third)
        {
            /* ... */
        }
    }
    


    Создадим экземпляр менеджера:

    $manager = Manager::create();
    


    IoC сам создаст экземпляры нужных классов для конструктора менеджера.

    Если нет PHP 5.4, то можно не использовать примеси:

    class Manager
    {
        public static function create()
        {
            return \IoC\Factory::create(get_called_class(), func_get_args());
        }
    
        /* ... */
    }
    


    Так же в конструкторе менеджера можно описать дополнительные параметры:

    class Manager
    {
        use IoC\Creatable;
    
        public function __construct(First $first,
                                    Second $second,
                                    Third $third,
                                    $value,
                                    $anotherValue = 'default')
        {
            /* ... */
        }
    }
    


    И по прежнему пользоваться IoC:

    $manager = Manager::create('value', 'another value');
    $managerWithDefault = Manager::create('value');
    


    В библиотеке реализованы три типа зависимости: Reference, Lazy и Prototype.

    Ленивая загрузка:
    $ioc->register('Foo');
    

    Экземпляр класса Foo будет создан только при вызове функции create()

    В данном случае, везде будет использован один экземпляр класса Foo:
    $ioc->register(new Foo());
    


    Если класс построен на шаблоне прототип (новые объекты создаются при помощи clone), то полезной будет следующая функция:
    $ioc->prototype(new Foo());
    

    При каждом вызове create() будет создан новый экземпляр Foo при помощи clone.

    Так же можно добавлять свои ассоциации:
    $ioc->assoc(new MyAssoc('Foo'));
    


    IoC позволяет вручную настроит соответствие реализации интерфейсу:

    $ioc->register('Foo', 'First');
    
    $ioc->register(new Foo(), 'Second');
    
    $ioc->prototype('Foo', 'First');
    


    Множественная ассоциация. В случае если класс FooBoo реализует сразу два интерфейса First, Second:

    $ioc->register('FooBoo', array('First', 'Second'));
    


    Так же можно добавить ассоциации для классов:

    // Если Boo extends Foo
    $ioc->register('Boo', 'Foo');
    


    Проект на GitHub

    Другие реализации IoC для PHP

    PHP порт Pico Container
    Phemto
    Sharbat
    Share post

    Comments 120

      0
      Нахуя это? Что за блять мода пошла тянуть всякое говно в PHP. Зачем усложнять себе жизнь?
        +2
        Это мощный и удобный инструмент. Данный порождающий шаблон очень удобен, если в приложении используется большое число интерфейсов и зависимостей.
        Почему бы не использовать его в PHP?
          0
          Приведите пример, я абстракциям не верю.
            +3
            Пример — Symfony2 фреймворк. В JAVA это Spring
              –4
              Ок, только для полной аналогии напишите свою PHPVM или хотя бы сделайте JPHP :)
                +2
                  0
                  Действительно, полная аналогия!
                    0
                    Вы наверное не совсем понимаете задачи JVM.
                    А sandbox нужен только если вы даете левым людям на сайте гонять команды PHP.
                      0
                      sandbox нужен что бы запускать там свои проекты. по крайней мере мы так его используем. Стоит заметить что у нас проекты являются php демонами.
              0
                +2
                Обратите внимание на flow3.typo3.org/ и DDD.

                Как пример, зачем может быть нужен dependency injection:
                — У вас есть два контролера в которых для обработки запроса нужно выполнить некоторые действия с объектом account
                — Для объекта account имеется объект AccountRepository который умеет по разному искать acount ( по id, по имени,… )

                Как вы получите ссылку на объект AccountRepository в контроллерах? В случае с DI Вам ни о чем беспокоиться не надо — ссылка будет вставлена в контролеры автоматически.

                Посмотрите, во тут есть больше информации по этой теме: flow3.typo3.org/

                –3
                Если в приложении используется большое число интерфейсов и зависимостей, то ваше приложение на PHP будет очень медленно работать.
                0
                Любители извращений незабудьте еще в карму насрать, у меня она резиновая.
                  +1
                  чуть не забыл, спасибо!
                    +2
                    welcome bro!
                      +1
                      Ладно уж, вернул в зад. Ну не стоит быть настолько категоричным!
                        –1
                        Нельзя быть таким мягким.
                          –1
                          да жалко беднягу… Быть может еще одумается =)
                  0
                  У меня на проекте очень часто проходится работать со сторонними сервисами. Например, в качестве CDN мы используем Amazon CloudFront, для рассылок mailchimp, для e-commerce Cleverbridge. А еще существуют разные CRM, с которыми тоже нужна интеграция. Так вот, при помощи DI, если правильно выделять абстракции, можно заменить любое звено в цепи. А если вы пишете тесты для своего кода, без DI вообще трудно обойтись.
                  +1
                  а можно целостный, приближенный к реалиям пример?
                    +2
                    IoC выстреливает на крупных проектах, идя рука об руку с TDD и другими enterprise прелестями. На простых примерах показывать это бесполезно.
                      +5
                      в языке с явной типизацией использование этого патерна оправдано, за счет того, что в случаи изменения не нужно править везде типы. Здесь же я боюсь, что это скорее замедлит выполнение скрипта, нежили облегчит разработку. Вы проводили бенчмарки?
                        +3
                        Причем тут вообще типизация? Менять типы при рефакторинге все равно придется, и IoC тут не поможет.

                        Разговаривать же о бенчмарках, обсуждая паттерн, который повлияет на всю архитектуру приложения, мягко скажем, некорректно. В данном случае намного важнее понимать чем и за что платишь. Похожая же ситуация и с XSLT, например.
                          –1
                          согл: все что замедляет работу скрипта нах…
                          из-за этого мы отказались от Фемты.
                            0
                            Купи новый сервер для скриптов, пиши намного более читабельный код, а не чуть быстрее работающий.
                              0
                              мой код и так читабельный
                              ни кто не жалуется.
                        • UFO just landed and posted this here
                            0
                            В статье я привёл простой пример. Думаю его должно быть достаточно что бы понят зачем использовать IoC. (Попробуйте инициализировать объект Manager без IoC)
                              0
                              Я, наверно, не понимаю чего-то важного, но без этих штук объект Manager, имхо, инициализируется как
                              new Manager(new Foo(), new Boo(), new Woo())
                              В чём преимущество-то? В том, что при создании менеджера нам хочется не знать, какие реализации используются? Так всё равно есть кусок кода с вызовами register(), в котором это придётся знать…
                                0
                                Нет, неправильно. Вот правильный вариант:

                                new Manager(new Foo(), new Boo(new Foo()), new Woo(new Foo(), new Boo(new Foo())))
                                

                                И классов может быть ещё больше. Данная библиотека успешно используется в дном проекте с огромным количество классов и значительно облегчает их генерацию.
                                  0
                                  И проблема в другом, например у вас есть иерархия классов, и как нижнем уровне вам нужен объект Foo, тогда вам придется передавать объект через все вышестоящие уровни.

                                  Естественно вы можете использовать pull подход и получить нужный объект прямо в конструкторе, но при использовании push подхода, IoC контейнеры облегчают жизнь.
                              0
                              Объяснить-то легко. Проблема в обосновании почему это вообще может быть полезно. Лично я не могу себе представить простого примера, где бы у вдумчивого читателя не возникло мысли «а нафига на это тратить свое время?»

                              А разговаривать о больших командах, низкой связанности кода, простоте тестирования и т.д. лично я простым языком не умею:(
                              • UFO just landed and posted this here
                                  0
                                  Я чуть-чуть о другом. Кроме плюсов всегда есть и минусы. И не всегда бывает просто объяснить почему и когда плюсы плюсов больше.

                                  Посмотрите хотя бы самый первый комментарий к топику. Между прочим, он вполне себе правилен. Зачем тянуть в язык, где не принято городить кучу абстракций, эти абстракции?

                                  Можно сказать о простоте тестирования и более низкой связанности кода. Но… Многих ли это убедит? Хотя, возможно, это просто я плохо умею выражать свои мысли.
                                    0
                                    Википедия может и Фаулер неплохо объясняет. К слову, синглтон уже давно считается антипаттерном :)
                                    • UFO just landed and posted this here
                            +10
                            Вобщем-то выглядит круто но непонятно для чего это надо.

                            Я уверен что все паттерны были придуманы во время решения какой-то конкретной задачи для которой этот паттерн лучше всего подходил. Причем паттерном я в данном случае называю не только какие-то идеи реализации а и конкретные «фичи» языка.
                            Например если вам нужно реализовать «принятие решения в зависимости от входных параметров» вам подходит паттерн «if-then-else».
                            Я сейчас очень упростил идею «паттернов», но и в классическом их понимании всегда подразумевается конкретное применение.
                            Например для синглтона есть отличный пример — драйвер работы с базой данных. Его всегда приводят в пример и всем сразу становится понятна суть.
                            Некоторые паттерны даже получили название в честь этого самого примера, как например «реестр».
                            Это я все к чему: Сколько не читаю про DI, все примеры откуда-то из космоса взяты.
                            Есть какой-нибудь хрестоматийный пример, а?
                              +1
                              Хрестоматийный пример:

                              У вас есть большой проект. В нем много классов моделей, которые мапятся на таблицы в базе данных. Раньше вы использовали синглтон для драйвера работы с базой данных и были счастливы. Но с увеличением нагрузки пришлось выполнить шардинг вашей базы данных. После шардинга из одной базы данных стало две. Теперь нужно два инстанса драйвера базы данных с различными настройками подключения. При этом в зависимости от модели должен возвращатся правильный инстанс базы данных. Понятное дело что синглтоном уже не обойтись. Вот тут и приходит на помощь паттерн DI.

                              Как это все выглядит?

                              Создается класс, например, DIManager. У этого класса есть метод getDbConnection( $modelName ), который в зависимости от имени модели возвращает инстанс соответствующей базы данных. Теперь все старые вызовы DBConnection::getInstance() заменяются на DIManager::getDbConnection( $modelName ). Таким образом мы ослабили зависимость модели от базы данных и теперь спокойно можем контролировать коннекшн к бд для любой модели в одном месте. Профит!

                              П.С.: Хрестоматийный пример IoC на PHP пока не придумал, но ворох интерфейсов и набор магических заклинаний мне кажутся избыточными и монструозными.

                              П.С.С.: Нельзя использовать паттерны только ради того что бы они были. Это ошибки неопытных программистов.
                                +10
                                Странный у вас шардинг, конечно… По имени модели работает.
                                Да и вообще-то синглтона в вашем примере более чем достаточно.
                                При необходимости осуществить конекшн на другой шард — создаем новый конекшн.
                                А его уже вызываем из синглтонов. Впринципе вряд ли у вас один скрипт будет последовательно писать на много шардов, а значит такой подход как раз пойдет.

                                Короче, променяли KISS на Keep it Enterprise
                                • UFO just landed and posted this here
                                    0
                                    Кстати, какие тогда вообще конкурентные преимущества в РНР? Если я усвою все эти паттерны и они мне понравятся, я просто уйду кодить на Джаве. Там платят больше. А принципы те же, только лучше.
                                      0
                                      Вот кстати, я думал на Джаве кодить, а за последние полтора года зарплаты в PHP для толковых разработчиков стремительно догоняют зарплаты программистов на Джаве. Передумал в общем пока что, потому что там с джуниора начинать опять уже не особо есть желание.
                                        0
                                        Но за полгода вы можете там достигнуть такого уровня зарплат, которых в РНР вы никогда и не увидите. По крайней мере, я слышал много таких success story. Люди меняли профиль, или просто устраивались на новое место джуниором, а потом быстро становились senior'ами и выше.
                                        • UFO just landed and posted this here
                                            0
                                            Возможно, не спорю.
                                            На самом деле, без разницы, на PHP или на Java, всё равно предел есть, который уже только карьерным ростом можно преодолеть, а не профессиональным.
                                              0
                                              В транс-национальном аутсорсере карьерный рост намного вероятнее, чем в стартапе или интернет-магазине.
                                              • UFO just landed and posted this here
                                          0
                                          Причем тут паттерны. Вы представляете себе объем информации по J2EE?
                                          Чтобы выйти на Senior Java Developer нужно как минимум знать: persistence, spring, servlets, ejb, maven,… иметь опыт с каким-нибудь application server и тд…
                                      +3
                                      с архитектурой шардинга ты что-то немного напутал. Мой шардинг имеет одно подключение к БД. Номер Подключения определяется еще на этапе инициализации Приложения в зависимости от входных параметров. Как-то так должно быть. Или на худой случай используем прокси соединение.

                                      Если у тебя 10 серверов БД — что будем держать соединений — бред сивой кобылы. А 40 серверов?
                                        0
                                        Это не случай из жизни, просто хотел на скорую руку привести пример DI.
                                        Если у вас есть пример DI лучше то милости просим, мы его с удовольствием обсудим.
                                          0
                                          у мня нет примера из жизни ДИ, просто у меня есть опыт разработки Hi-проектов, в том числе организации шардинга.
                                        +1
                                        все старые вызовы DBConnection::getInstance() заменяются на DIManager::getDbConnection( $modelName )

                                        И что изменилось? Была статическая зависимость на DBConnection, стала на DIManager. При этом DIManager к внедрению зависимостей имеет отношение только двумя буквами в названии.

                                        Для решения задачи используется либо регистр, который делается синглтоном, либо более продвинутая реализация — сервис локатор.
                                          +1
                                          Скорее всего вы правы, это именно сервис локатор. Немного не удачный я выбрал пример что бы объяснить DI
                                        0
                                        Паттерны не зависят от языка.
                                          0
                                          Я не против, я лишь слегка расширил концепцию.
                                          Например трейты в пхп 5.4 я тоже считаю паттерном.
                                          +5
                                          DI — очень гибкий паттерн. Вы, возможно, удивитесь, но он может заменять даже синглтоны (которые в «чистом» виде являются злом в большинстве случаев, например — в вашем применении к работе с БД). Как-то так это может выглядеть:

                                          $di->addService('MyDbConnection', '\My\Super\MySqlConnector');
                                          $di->addClass('ClassThatUsedDb', '\My\Super\Class', array('&MyDbConnection')); // 3й аргумент - массив параметров конструктора
                                          // ...
                                          $di->newInstance('ClassThatUsedDb')->makeWorkWithDb();
                                          


                                          Теперь рассмотрим такой пример. Допустим, вы приняли стратегическое решение перейти с MySQL на Postgre. Что надо сделать в случае простого синглтона? Написать новый класс-синглтон, найти все вхождения в коде, включая все классы, использующие его (которых может накопиться ой как много), и переправить все на новый класс. Что надо сделать в нашем примере? Заменить одну строку:

                                          $di->addService('MyDbConnection', '\My\Super\PostgreConnector');
                                          


                                          (естественно, предполагается, что интерфейсы классов MySqlConnector и PostgreConnector совпадают).
                                            0
                                            Если интерфейсы классов совпадают мне не нужен никакой DI.

                                            В этом случае мне гораздо проще использовать Adapter как это сделано в Zend_Db
                                              +1
                                              Если уж говорить о паттернах, то, в Zend_Db используется «абстрактная фабрика», если я правильно понимаю. Паттерн «адаптер» как раз организует совместную работу классов с несовместимыми интерфейсами.

                                              Ну а DI может заменить вам и абстрактную фабрику, если уж на то пошло. Говорю же, гибкий паттерн :) Однако я не настаиваю на его использовании везде и повсеместно.
                                                0
                                                Ну так он этим и занимается. Есть например mysql_*() и pg_*()
                                                Ну да, ето не классы а наборы функций, но ето не суть. Вот он (адаптер) и организовывает их работу в один интерфейс.
                                                Абстрактная фабрика там хоть и используется но решает другие задачи а не ту которую вы описали (проблема переключения между двумя базами данных).

                                                Паттерн конечно гибкий я это уже заметил, применить можно где-угодно. Но это не то что я интересно.
                                                Хрестоматийный пример это когда без паттерна пришлось бы его (паттерн) изобрести.
                                                А тут какой пример ни приведут все время есть варианты с более узкоспециализироваными решениями которые очевидней или просто «понятней» синтаксисом.
                                                Вопщем требую адекватных примеров! :)
                                                  +2
                                                  Ну так он этим и занимается. Есть например mysql_*() и pg_*()

                                                  Опять же не совсем точно, там используется PDO, которое уже обеспечивает абстракцию от конкретных функций конкретной БД. Но это, не суть, я понял, что вы имеете ввиду. В моем изначальном примере MySqlConnector и PostgreConnector тоже могут быть адаптерами.

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

                                                  а) делает код менее связанным, за счет того, что классы не создают внутри себя экземпляры других классов, а хранят тока ссылки на объекты нужных классов, которые поставляет им контейнер;
                                                  б) дает возможность собрать всю информацию о связях между классами в одном месте, где удобно впоследствии будет вносить изменения, причем, это зачастую можно сделать декларативным способом (как пример — в симфони можно использовать для описания YAML или XML);
                                                  в) нужные классы-сервисы инстанцируются только когда нужны, и автоматически инстанцируются все связанные классы. Например: где-то вначале своего кода вам захотелось сконфигурировать работу БД, вы пишете что-то вроде этого:
                                                  $db = Zend_Db::factory($this->_config->db);
                                                  // Задание адаптера по умолчанию для наследников класса Zend_Db_Table_Abstract 
                                                  Zend_Db_Table_Abstract::setDefaultAdapter($db); 
                                                  

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

                                                  Разумеется, не следует пихать в контейнер абсолютно все классы вашего проекта. Лично я придерживаюсь такого подхода: классы из одного пространства имен знают друг о друге и используют друг друга напрямую, а в DI описываются только некие классы-сервисы (вроде того же подключения к БД, или какого-нибудь Mail'ера и т.п.), которые широко используются в разных частях проекта.
                                              0
                                              Все это круто, конечно же, но DI уместен скорее при разработке универсального фреймворка нежели частного проекта. Почему? Потому что при разработке частного проекта вы знаете и уверены в том что вам нужно для разработки, и по этому у вас не встанет вопрос перехода с одной СУБД на другую. По этому в частном проекте вы будете тратить время на реализацию реальных задач, а не на нагромождение абстракций с целью обезопасить себя на тысячу лет вперед от всех возможных случаев жизни.

                                              С другой стороны, если вы разрабатываете открытый фрейморк, которым будут пользоваться не только вы и ваша команда (аля symfony, zf, ci, yii, etc), то вы не будете уверены в том что именно захочет сделать пользователь вашего фремворка. И тут вам придется городить огород из махровых абстракций, что бы угодить всем и вся.
                                                0
                                                Абсолютно согласен. Я и не агитирую за использование DI в частных проектах, просто попытался прояснить человеку, какие можно получить выгоды от использования.
                                                  0
                                                  Оо, вот это уже что-то. Если честно, для меня сейчас совершили открытие тем фактом что пользы от DI в частных проектах мало.

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

                                                  Мне кажется что в статьях про DI это надо писать в первых абзацах. Что то типа раздела «Цели и решаемые задачи».
                                                    0
                                                    Все зависит от того, что у вас за частные проекты :)

                                                    Быть может, вам не нужно будет самому реализовывать этот паттерн, но можно получить выгоду от использования готового решения, например в том же Сифони
                                                0
                                                Случай, когда меняется класс, но не меняется инерфейс не так уж сложен. Сложен он когда меняется интерфейс и тут уже ни DI, ни IoC, ни что иное не помогут.
                                                Тут может помочь только модель событий, с помощью которой можно будет подменять данные, при вызове устаревшего класса. Но это возможно только в сучае изначального внедрения такой модели в проект.
                                                Например, у меня используется система, при которой на одно и тоже событие могут отреагировать несколько классов и решение о том какой именно отреагирует система принимает в зависимости от состояния.
                                              +2
                                              А что по поводу этой библиотеки от Sensio которая используется в Symfony2
                                                +3
                                                Эта версия используется в Symfony1 и более не поддерживается. В Symfony2 используется вот эта: github.com/symfony/DependencyInjection
                                                  0
                                                  ета версия библиотеки подходит также и к php 5.2, котого еще ох как много где
                                              +2
                                              Как то дурно это все пахнет. Какой то неявный Registry, что наводит на мысли что магия это зло, если это не возможность языка. Это как с аннотациями, когда начинают парсить аннотации с комментариев в скриптах, блевать хочется.
                                                +2
                                                Ну если есть хорошее описание аннотаций, которые будут парситься, то все очень даже удобно. Doctrine тому пример.
                                                DI — может быть очень полезна для сложных проектов и лечь в архитектуру проекта. Но тут, как говорится, главное не переборщить.
                                                  +3
                                                  >> то все очень даже удобно
                                                  Это ваше субъективное мнение. Не только я лично отказываюсь от этой ОRМ в частности из-за этого. Считаю полнейшим бредом добавлять возможности на основе комментариев (строк) в язык. Если это считать как ничего страшного то можно дойти до того что будут реализовывать декораторы в манере python только с помощью строк.
                                                  Насчет DI тут кому как нравиться, у меня руки не отвалятся написать явный реестр явно инициализированных объектов, а не городить обертку ради очень сомнительной выгоды в динамическом языке. Вы можете ссылать на какой то абстрактный «большой» проект, но лучше если бы вы реально написали какой плюс от DI на реальном примере.
                                                    +1
                                                    >> явный реестр явно инициализированных объектов
                                                    Вот это место и является минусом из-за инициализации на каждом запросе всех нужных и ненужных объектов.
                                                    В DI это решается контейнером с лямбда функциями.
                                                    Другое дело, что вместо DI можно обойтись ServiceLocator, но тут уж как кому удобно.
                                                      +1
                                                      Так можно как понадобится инициализировать, а не сразу.
                                                        +2
                                                        Я использую Lazy initialization, Proxy, Registry, причем от третьего иногда отказываюсь. DI я использовал и у меня появлялись постоянно преграды в рефакторинге, а когда нужно быстро сделать прототип проекта это занимает очень много время в Lazy initialization или Proxy мне приходится переписать всего один метод в случае изменений и поверьте с головой хватает, причем все вещи сделаны явно и производительность не страдает совсем(!).
                                                          +1
                                                          P.S. Замыкания по сути тоже инициализация, только объекта класса Closure. Безусловно неким количеством памяти можно пожертвовать на это но зачем? Я просто выгоды не вижу. Да получается статика, но явная статика ты полностью уверен что метод возвращает нужный тип/класс.
                                                          А вообще задача решает все. Но ИМХО в 98% DI в PHP вообще не нужен и постоянно брать его за основу для «больших» проектов можно только по очень большой приверженности к нему.
                                                            +1
                                                            Остальные 2 процента — это тестирование. Потому что в реальной практике я могу вспомнить пару примеров, когда подмена нужна была, но оверпрограмминг с использованием DI больше чем все эти случаи вместе взятые. Хотя я и использую вариацию на тему — сервис локатор, при этом он задевает малюсенькую долю инстанцирования.
                                                      0
                                                      А это как раз возможность языка: ReflectionClass.
                                                        +1
                                                        Service Locator (или Registry) работают по pull принципу, т.е. классы сами вытягивают зависимости и имеют статическую зависимость от, собственно, Service Locator. В случае IoC контейнера внедрение происходит по push принципу, т.е. с точки зрения классов инъекция происходит через конструктор / сеттер.

                                                        Профит:
                                                        — отсутствие статической зависимости;
                                                        — упрощение модульных тестов.
                                                        0
                                                        Магия только в примесях. Все понравилось. Тем, кто ничего не понял читать в таком порядке:
                                                        Устранение зависимостей в коде.
                                                        Паттерн фабричного метода.
                                                        Идеалогия интересного программирования.
                                                        Книга: чистый код.
                                                        Както так.
                                                        Теперь пример: вы делаете фабрику машин, и подшипники вам поставляет Бош, а втулки Сименс (реализация интерфейса) и теперь вам не нужно обучать ваши агрегаты работать с этими деталями.
                                                          0
                                                          интересный пример, нельзя ли поподробнее?
                                                          то есть с помощью этой магии втулки от Сименс в любом случае подойдут к подшипникам Бош?
                                                            +2
                                                            Если бы Сименс делали втулки на PHP они бы разорились минуты через две после начала выпуска 0.о
                                                              0
                                                              ага, теперь я понял, нам не нужно обучать наши агрегаты работать с этими деталями, так как в агрегатах не используется PHP, чорд, так и думал!!! спасибо ;)
                                                          –1
                                                          Смайл. Вы решили проблему, но ваше решение и породило эту же проблему. теперь у вас нет возможности подменить ioc. Нужно избавляться от Singleton и такскать инстанс, например, инъекцией в конструктор.
                                                          p.s. Сам пользуюсь смешанным подходам, но идеологически верно без него.
                                                            0
                                                            Идеологически верное решение породит довольно неудобный для повседневного использования синтаксис.
                                                              –2
                                                              В этом и печалька, не всё правильное является удобным.
                                                              Вот вы в Yii используете много статики и это удобно в сравнении с правильным Zend Framework, где для любого чиха нужно не менее десятка классов, затем ещё настроить длиннющую цепочку вызовов. А в итоге получается уродец Zend_Db_Table, недавно писал модуль учёта финансов с использованием подхода из quickstart, очень советую попробовать, а лог профайлера бд лучше не включать.
                                                                0
                                                                У нас статики, кстати, не так много: DI-подобный контейнер Yii::app(), фабрики моделей CActiveRecord::model(), ну и остальное совсем по-мелочи.
                                                                0
                                                                ну есть же такая вещь как autowiring. ну и имплементация его на PHP совсем не одна. и синтаксис в таком случае доооовольно удобен. так что не надо ето…
                                                                  0
                                                                  Удобнее, конечно, чем голый DI. Тут не поспоришь.
                                                              +6
                                                              А вы не смотрели ли на Pimple? Всего то 149 строчек кода и практически весь функционал DI есть. Все просто.

                                                              Если же кому нужно множество примеров с применением DI/IoC именно в PHP — тогда, пожалуйста, вам сюда.
                                                                0
                                                                Отличная презентация, очень всё понятно и аргументировано. Есть ещё что посмотреть в таком же качестве?
                                                                +1
                                                                Можете пояснить, почему на 62-м слайде сказано, что при

                                                                        $container->user = function($c){
                                                                                static $user;
                                                                                if(is_null($user)) {
                                                                                        $user=new User($c->storage);
                                                                                }
                                                                                return $user;
                                                                        };
                                                                


                                                                истинным является подобное выражение:

                                                                spl_object_hash($container->user)!==spl_object_hash($container->user)


                                                                У меня наоборот, хеши объектов равны, да и из кода это очевидно. В чем я ошибаюсь?

                                                                  0
                                                                  Насколько я понял, это до реализации 63-го слайда объясняется ситуация. После реализации они равны и это видно дальше на слайде 64.
                                                                    0
                                                                    Хм. Понятно. Несколько неоднозначно.
                                                                –1
                                                                Вкратце выскажу мнение. Скорее всего DI в том виде в котором его подают не нужен для большинства задач.
                                                                Он нужен для разработчиков библиотек, продуктов и пр. Для веб-сайта и веб-приложения как раз не столь необходим.

                                                                Суть Dependency Injection ясна и правильна, но вот контейнеры это зло. Потому что если вдруг оказалось, что какой-то класс мы забыли добавить в контейнер, то нужно рефакторить весь код и заменять все вызовы. А зачастую так же все и происходит. Сначала у вас класс простой, а потом он разрастается, появляются зависимости, их нужно конфигурить… И в момент, когда вы понимаете, что вам нужен DI для этого класса, необходимо рефакторить код. Короче, какая-то дедукция в разработке получается.

                                                                А ещё зло эти ваши фабричные методы. Никогда не знаешь что оно вернет и с какими методами/свойствами. То как реализованы в доктрине вызовы репозиториев, это вообще печаль — никогда не знаешь какие методы в этом репозитории. Каждый раз нужно отдельно открывать файл репозитория и проверять сигнатуры нужных функций.
                                                                  0
                                                                  it depends.
                                                                    +4
                                                                    необходимо рефакторить код
                                                                    Код необходимо рефакторить всегда, до этого момента, после этого момента, вместо этого момента и даже вместе с этим моментом :)
                                                                      0
                                                                      Ага, не решать конкретные задачи, а рефакторить код. Господа, так не бывает.
                                                                      Есть бизнес-задачи, есть рефакторинг. Бизнес-задачи приоритетнее. ТОт паттерн эффективнее, что помогает избегать рефакторинга, а не тот, что провоцирует его. Иначе вообще зачем нужны паттерны, если в случае чего мы будем перестраивать архитектуру?
                                                                        +5
                                                                        Если для решения конкретной задачи требуется изменить 5 классов и наговнокодить, а задачу нужно решить прямо сейчас, то обычно говнокодят и ни завтра, ни послезавтра никто не рефакторит. А потом еще наговнокодят и опять не рефакторят. Поэтому, я считаю, нужно рефакторить сразу, независимо от того, насколько срочная задача. Появление бизнес-задач, которые нужно решить прямо сейчас — ошибка менеджмента, планирования, etc.
                                                                        Рефакторинг — это часть любой бизнес задачи. Не бывает кода, который когда-то не придётся изменить в связи с новой бизнес-задачей, как и не бывает кода без багов. Можно ведь и баги не исправлять, если новые бизнес-задачи приоритетнее.
                                                                        Без рефакторинга любая бизнес-задача с каждым днём будет отнимать всё больше времени. Процесс разработки любого продукта и новых фич должен включать в себя рефакторинг.
                                                                        А ещё, для эффективного, быстрого и качественного рефакторинга нужно писать тесты, писать тесты!!!, ПИСАТЬ ТЕСТЫ БЛЕАТЬ!!!111 :)
                                                                          –1
                                                                          Ок, но какая основная мысль?
                                                                          Мы можем начать писать сайт как <?php echo "Our Site" ?>
                                                                          а потом при усложнении бизнес-задач его рефакторить?

                                                                          Наверняка нет. Если вы рефакторите — значит нынешняя архитектура недостаточно продумана или просто устарела для решения текущих задач. Если есть возможность создать расширяемую архитектуру, и нововеденья внедрять в неё без гавнокода, значит не нужно рефакторить после каждого чиха.

                                                                          Архитектура тем лучше чем меньше в ней надо будет рефакторинга.
                                                                          Ну а я привожу пример того, что DIC в этом плане недостаточно хорош.
                                                                            +2
                                                                            Рефакторинг всё равно будет, независимо от того, как продумать архитектуру.
                                                                            Даже отличный архитектор ПО никогда не может всего предусмотреть.

                                                                            А вообще, тут немного разные подходы к разработке обсуждаем просто. Я с уклоном в Agile, а у вас больше про полный жизненный цикл с серьезным подходом именно к проектированию приложения. И то, и другое, имеет право на жизнь, только во втором случае обычно есть четко формализованное ТЗ, которое всё равно уточняется в процессе проектирования, а в первом обычно нечего проектировать, так как требования к продукту меняются быстрее, чем их реализовывают.
                                                                              –1
                                                                              Будет. Но разница только в том сколько времени на него тратится. Если это время можно сократить (не в ущерб качеству кода), значит больше времени остается на решение бизнес-задач.

                                                                              +2
                                                                              Рефакторинг — это изменение реализации, без изменения внешнего поведения. Т.ч. рефакторинг по определению не может изменять архитектуру :)
                                                                                +1
                                                                                0.o

                                                                                Как отношение имеет архитекутура к внешнему поведению?
                                                                                  +1
                                                                                  Архитектура — набор компонентов системы и взаимодействие между ними. Рефакторинг компонента не затрагивает его поведение, а меняет только реализацию.
                                                                                    0
                                                                                    Есть внешний API и внутренняя реализация.
                                                                                    Архитектура это обычно как раз построение реализации.
                                                                                      0
                                                                                      При чем здесь API? Графическим представлением архитектуры системы будет UML модель, на которой отражены компоненты, классы и взаимодействие между ними. О конкретной реализации классов архитектура ничего не говорит.

                                                                                      В первом комментарии я немного погорячился. Точнее будет сказать, что рефакторинг существенно не влияет на архитектуру. Он модифицирует реализацию классов / компонентов, обычно незначительно затрагивая интерфейс.
                                                                                0
                                                                                Ок, но какая основная мысль?
                                                                                Мы можем начать писать сайт как <?php echo «Our Site» ?>
                                                                                а потом при усложнении бизнес-задач его рефакторить?


                                                                                Про это не сказал. Если сегодня задача стоит вывести на странице «Our Site», то я так и сделаю, как в примере. А завтра будет новая задача — естественно рефакторить.
                                                                                  0
                                                                                  После появления новой задачи ты будешь просто программировать :)
                                                                          0
                                                                          Всё верно, такой подход оправдан в сложных, модульных приложениях, где требуются точечные изменения кода, но без возможности непосредственной правки классов. Контейнер позволит просто подменить экземпляр на свой класс, с сохранением интерфейса.
                                                                            +1
                                                                            Если фабричный метод возвращает интерфейс, то никаких проблем нет. Клиенту не нужно знать о реализации. Это один из основных принципом ООП: инкапсулирование.
                                                                              0
                                                                              Не то что никаких… Скорее, их чуть меньше. В примере с репозиториями доктрины, ок, интерфейс есть. От этого легче? Нет. Всё равно сигнатура кастомных функций неизвестна. И вперед бегать по всем файлам, открывать тот самый репозиторий, смотреть его функцию… В интерфейсе может быть вообще одна функция, а в реализации их десть-двадцать. И потому интерфейсы не особо помогают :(
                                                                                0
                                                                                Работа в доктрине с ренозиториями не лучший пример. Мне самому это не нравится.
                                                                                Но думаю вы не будете отрицать, что абстрауная фабрика это один из действительно полезных порождающих паттеров.
                                                                                  0
                                                                                  Фабричный метод и абстрактная фабрика — это как бы разные шаблоны.
                                                                                0
                                                                                Этот принцип называется полиморфизм ;)
                                                                                  0
                                                                                  Скрытие реализации — это инкапсулирование.
                                                                                    0
                                                                                    При чем здесь скрытие реализации и фрабричный метод?

                                                                                    Для того чтобы система оставалась независимой от различных типов объектов паттерн Factory Method использует механизм полиморфизма — классы всех конечных типов наследуют от одного абстрактного базового класса, предназначенного для полиморфного использования. В этом базовом классе определяется единый интерфейс, через который пользователь будет оперировать объектами конечных типов.
                                                                                      0
                                                                                      Описывая интерфейс, вы скрываете реализацию (инкапсулирование). Затем для генерации экземпляров вы используете фабричный метод (полиморфизм).

                                                                            Only users with full accounts can post comments. Log in, please.