Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > PHP: Общие вопросы > Паттерн Внедрение Зависимости |
Автор: solenko 16.7.2009, 17:39 |
Не так давно появился сайт http://components.symfony-project.org, на который авторы фреймверка symfony выкладывают библиотеки, которые написаны в процессе разработки фреймверка, но могут быть использованы отдельно. Один из таких компонентов -- Symfony Service Container. Это компонент, который реализует паттерн Контейнер Внедрения Зависимостей. Также была написана небольшая http://components.symfony-project.org/dependency-injection/trunk/book/, которая описывает принципы работы компонента. Книгу стоит почитать даже если вы не собираетесь использовать компонент -- первые главы описывают сам паттерн, а не конкретный компонент. Мой вариант превода книги: http://madbee.ukr.su/solenko/dependency-injection/ ksnk и Sanchezzz - очень рекомендую для прочтения (после тем о загрузке классов). Буду благодарен за критику перевода -- как-никак первый опыт. |
Автор: ksnk 16.7.2009, 18:02 |
solenko, Там есть орфографических ошибок. Можно внедрить систему http://orphus.ru/ и корректировать прямо по прочтении... А вообще - спасибо! Не дочитал, но пока - интересно ![]() |
Автор: ksnk 16.7.2009, 18:30 | ||
Паттерн, вообще говоря, требует перепроектирование уже спроектированной системы :-( К примеру, разнообразные плагины CMS'ки в редкие моменты своего функционирования желают воспользоватся функцией mail. Мы, как умные ООП программисты обернули mail в показавшуюся нам удобной в тот момент обертку. Строки, работающие с классом выглядят как
В дальнейшей жизни нам показалось недостаточной реализация mail и родилась обертка mail2. Рассматриваются варианты внедрения новой обертки в уже существующие тексты плагинов с минимальной правкой исходного кода... Рассматриваемый паттерн предлагает нам подставлять уже проинициализированный элемент класса mail2 в объект, не взирая на то, что этот класс реально может и не понадобится? Это ли тру ООП? |
Автор: ksnk 16.7.2009, 19:15 |
в статье Контейнеры Внедрения Зависимостей автор путает паттерн фабрика с паттерном "Внедрения зависимостей" ![]() Вообще, это паттерн-ориентированное программирование - тупик. Нужно сначала писать код, а потом уже угадывать в его контурах тот или иной паттерн ![]() |
Автор: ksnk 16.7.2009, 20:06 |
в статье - это чистой воды фабрика. объект пользователь спрашивает у объекта фабрики - "Дай мне экземпляр объекта mail"... То что этот вопрос формулируется в виде вызова функции контейнера никак не влияет на фабричность... ![]() Польза Dependency Injection в том, что есть единственное место (сонфигурационный файл), в котором в экземпляр - контейнер вставляют необходимые ему для работы уже проинициированные компоненты. А естл эти компоненты сокрыты в непонятно где лежащем отдельном объекте контейнер, "инициализация" которого делается типично фабричным методом, то и называть такой контейнер стоит фабрикой... |
Автор: solenko 16.7.2009, 20:14 | ||
Вот только не просто создается объект mail (фабрика), а еще и выполняется его конфигурирование -- создается объект mail_transport (контейнер). Основная разница как раз в том, что фабрика подразумевает самоконфигурирование объектов, а контейнер -- конфигурирует их. Добавлено через 2 минуты и 28 секунд Кроме того, фабрика порождает объекты какого-либо одного класса (т.е. возвращает экземпляры подклассов одного класса), а контейнер работает с произвольными классами. Хотя ключевая разница -- конфигурирование. Добавлено через 5 минут и 1 секунду Кстати, перефодом я занялся после прочтения http://habrahabr.ru/blogs/php/64061/ Там рассматривается разница фабрики и контейнера. |
Автор: youri 16.7.2009, 21:45 | ||
и какой смысл "потом" в паттернах? Это какое-то антипаттерн-ориентированное программирование ;) |
Автор: ksnk 16.7.2009, 22:47 | ||||||||
youri, Ну дык откуда взялись-то паттерны? Просто их "отцы-создатели" анализировали большое количество кода и обнаружили, что приемы программирования, вообще говоря можно классифицировать. Примерно та-же фигня, как и в биологии. Типо - хордовые, покрытосемменные, парнокопытные... Сильно обрадовались биологи и начали все обзывать по новой классификации, ан не тут-то было. природа она завсегда всякой классификации найдет свое исключение... ![]() "Здравый смысл"-ориентированное программирование!
Да ну? Вот квинтэссенция статьи "Контейнеры Внедрения Зависимостей". Тру-ООП код, размещенный предпоследним (последний пример - несущественная для ООП мутация в сторону синглтона)
Чем, скажите мне, этот код не фабрика? "Фабричный класс" имеет четко определенный (пустой) конструктор. Сам конструктор обязан инициализироваться, выковыривая из некоей "регистри" собственные параметры. Чужой дядя, обращается к контейнеру со словами "getMailer" - дай мне почтальона... Причем тут еще какой-то Dependency Injection? Тем что этот код мутациями и издевательствами из него трансформировали? Добавлено через 12 минут и 23 секунды А в статейке-затравке мне понравился вот такой код-конфигурация:
Есть в этом некая благородная креза! Хотя обильное повторение некоторых слов портит все впечатление... А вот если переписать этого монстра с помощью ассоциированного массива, может получится радующая мой глаз конструкция
|
Автор: Sanchezzz 17.7.2009, 08:16 | ||
По мне фабрика лучше выглядит, когда ты только передаешь названия нужных тебе для подключения классов и через другую функцию получаешь нужный тебе экземпляр или на прямую через массив классов. Меньше леса получается да и читается код с легкостью |
Автор: Sanchezzz 17.7.2009, 08:42 |
прочитав до конца статью, я кое что вывел полезное для себя, пока это объяснить не могу а покажу кодом , сча попробую накатать велосипед (очередной). |
Автор: youri 17.7.2009, 09:11 | ||||
а кто-то говорит, что нужно насоздавать паттернов на все случаи жизни? Кроме того, мне кажется, что паттерны скорее относятся к проектированию, чем к написанию кода. Вот у тебя в разработке есть такой этап как проектирование?
я просто не понимаю, зачем после написания кода угадывать в нем какие-то паттерны. И для чего тогда паттерны вообще? |
Автор: ksnk 17.7.2009, 10:57 |
Вот у меня в разработке есть такой этап как перепроектирование на ходу ;-) если проект живет достаточно долго, для него, imho, в обязательном порядке наступит такой этап. Продуманная изначально архитектура позволит пережить этот этап с допустимыми потерями. А вообще, раз и навсегда спроектированные приложения, это такая-же абстракция как и чистые, незамутненные паттерны и сферические кони. Они никому не нужны, так как не существуют в природе. А паттерны нужны для анализа получившегося кода на этапе "доводка напильником после проектирования". Паттерны - достаточно удачные решения в каких-то ситуациях. Их нужно знать Можно "поугадывать контуры" в реальных монстрах и, если это принесет пользу, "откорректировать мутацию"... |
Автор: youri 17.7.2009, 11:29 | ||
а где я говорил, про навсегда спроектированные приложения? Ведь придумали же такую штуку как проектирование... навреное не просто так... или ты думаешь, что думать перед тем как что-то делать - абсолютно бессмысленное занятие? |
Автор: icewind 17.7.2009, 12:27 |
solenko, спасибо. Пока что бегло просмотрел. Чуть позже прочту полностью. |
Автор: ksnk 17.7.2009, 13:02 |
youri, Вопрос "проектируете ли вы свое приложение?" напоминает мне "как часто вы бьете свою жену?". Какой ответ предполагается услышать? ![]() |
Автор: Sanchezzz 17.7.2009, 13:10 |
![]() ![]() youri,да тут дураку понятно что "без проектирования и мысленного представления как это будет работать далеко не удишь." |
Автор: fesor 17.7.2009, 13:26 |
Штука интересная но неимет смысла ее использовать. |
Автор: youri 17.7.2009, 13:30 | ||
вроде ж ты ответил уже просто я тебя понял так, что проектирование - ненужная вещь |
Автор: solenko 17.7.2009, 13:30 |
fesor, а обосновать? |
Автор: fesor 17.7.2009, 14:29 |
Ну например тм рассматривается пример: В класс системы пользователей передают 2 класа: для работы с БД и управление сессиями. Там же используется такой код: $this->db->execute к примеру. Это уже задает ограничение на интерфейс класса (ну тобиш названия методов меняться не будут уже. Можно только добавить новых а иначе снова искать и убирать лишнее). Почему бы тогда не воспользоваться чем-то вроде system:: db()->execute() (кстати это довольно похоже на класс-контейнер) или db::execute() вообще... Ну со своим высказыванием я погорячился, этот паттер много где встречается и в моих проектах, просто я незнал что это типа "фишка" (я конкретно про контейнеры). А так это надо знать чтобы потом повыпендриваться. Всеравно насколько я знаю мало кто придерживается каки-хто святых стандартов и паттернов когда у вас дедлайн через 3 часа) |
Автор: ksnk 17.7.2009, 14:51 |
Ненужная вешь - проектировать "под паттерны". Нужно расписывать задачи, которые предстоит решать и представить себе пути их решения. Потом решение можно откорректиовать, имея ввиду уже наработанный багаж решений, которые зазываются "паттернами". Впрочем это уже философия и метафизика... Оно все само как-то поолучается ![]() |
Автор: solenko 17.7.2009, 15:27 | ||||||
Оограничения по интерфейсу есть всегда, даже в разговорной речи. Главное чтобы при смене реализации этот интерфейс не менялся.
system:: db()->execute() -- вводятся новые зависимости. Теперь ваш класс зависит от некоего system, от db и от того проинициализирован ли класс db в system. db::execute() -- кто занимается инициализацией класса db? Каким образом работать с несколькими базами данных? Добавлено через 6 минут и 35 секунд
Dependency Injection в данном случае -- установка класса транспорта. Не все то, что пораждает объекты, зовется фабрикой. Суть фабрики в том, чтобы прозрачно для клиента решить какой из подттипов объета вернуть. Фабрика не работает с любыми типами объектов. То что вы приводили в темах о подключении модулей к фабрике имеет слабое отношение. Суть контейнера в том, чтобы: 1. Создать все объекты-зависимости 2. Создать сам запрашиваемый объект 3. Выполнить инициализацию объекта, передав все зависимости и начальные настройки. Фабрика из всего этого выполняет лишь второй пунт. |
Автор: youri 17.7.2009, 17:00 | ||
т.е., обдумывая пути решения задачи, мы должны отбросить пути решения с помощью паттернов (даже если знаем, что их надо будет потом использовать). Это меня, собственно, и смущает. Это твоя позиция? |
Автор: ksnk 17.7.2009, 17:14 | ||
Тутошний контейнер не создает, а знает как создавать. Разница, в некоторых случаях значительная...
В каком месте в отквоченном мной коде производится инициализация объекта? инииализацией объекта занят сам объект... Ну вот, нужно начинать с определений ![]() Следует иметь ввиду, что "паттерны программирования", а не "паттерны ООП программирования". То есть в определении слово "класс" и "объект" должно иметь значение "черный ящик", с определенным списком свойств. К примеру: Регистри - ящик, имеющий 2 функции. -- по имени (возможно строковому, но не обязательно) и значению сохраняет где-то в себе эту пару, -- по имени может выдать сохраненное значение. Примерная дисциплина использования - любой объект может чего-то сохранить, любой объект может чего-то запросить... Фабрика - это регистри, отягченная ограничениями. -- не все имеют право (кроме специально обученных объектов-конфигурации) изменять соответствие имя-значение. Фабрика объектов - выдаваемое фабрикой значение - объекты с унифицированным ( с одинаковым смыслом и порядком параметров) конструктором. |
Автор: solenko 17.7.2009, 17:20 | ||
вот в этой: Сам объект не запрашивает какой ему пранспорт использовать. |
Автор: ksnk 17.7.2009, 18:13 |
Это я упустил ;-( Однако так получается, что у нас знания о классе - мейлере размазаны по большому количеству кода. если вообразить, что нам понадобился класс, реализованный вообще без параметров (на встроенном mail'е) нам придется править конфиг $container = new Container ... (ну это уж необходимо) и придется править метод getMailTransport в описателе некоего контейнера неизвестно где описанного... Это я про то, что если проявить должную дисциплину при создании обертки, получившийся фабричный элемент был бы более Тру-ООпным. Править пришлось бы только в одном месте, в конфигурации... |
Автор: solenko 17.7.2009, 19:03 | ||||
Знания о нем сосредоточены в одном месте -- контейнере. А в идеале -- файле-конфигурации контейнера. Собственно, в этом и смысл контейнера.
Вообще-то достаточно удалить вызов $mailer->setDefaultTransport(). При использовании Symfony Service Container -- удалить одну строку его конфига. |
Автор: fesor 18.7.2009, 00:11 | ||
Ну допустим класс system является главным в системе и в него все загружается (как в контейнер). Допустим где-то понадобилось вызвать базу данных. Сделав доступ к функции db() класса system мы смотрим, загружен ли класс для работы с БД и если нет - загружаем. Ну и отдаем его. Мне такая реализации кажется достаточно простой и не напряжной в плане расширения. |
Автор: solenko 18.7.2009, 04:33 |
В какой системе? В той, которую вы пишите именно сейчас? А как испоьзовать ваши классы, расчитанные на system Sanchezzz-у, который любит называть свой мегакласс, содержащий все, Core? Прелесть внедрения зависимостей как раз в том, что класс не знает с каким именно классом он работает -- важен интерфейс. И прелесть контейнера в том, что класс не знает о его существовании -- нет зависимости от контейнера. |
Автор: fesor 18.7.2009, 08:49 |
Ну ладно... тогда стоит рассмотреть этот вариант. |
Автор: Sanchezzz 20.7.2009, 08:04 | ||
solenko мои классы и работаю и отдельно вне зависимости от моего мега-класса которого я люблю называть "core", бери и подключай. Мой "core" не случайно называется так, при разработки велосипеда мои задачи были такие: Нужно было сделать класс который будет подрубать классы и раздавать что первое и получилось. Затем нужно было создать в ядре функцию которая будет отвечать за подключение зависимых классов, такие классы служат в роли компонентов которые отвечают за контент: новости, блоги, форумы итд. Также эти компоненты получают доступ к функциям ядра и способность использовать подрубленные классы или подключать их. Минус в них то они имеют обязательных 2 функции одна из ник подключение шаблона логики для вывода информации и функция вызова, которая готовит данные для шаблона логики. Так выглядит у меня зависимый класс
Сча я стараюсь доделать это дело до совершенства сделать как можно меньше зависимости. |
Автор: fesor 20.7.2009, 09:06 |
код очень похож на мой первый фреймворк) |
Автор: Sanchezzz 20.7.2009, 09:18 | ||
ну это не фреймворк это кусок компонента. (зависимый класс от ядра ((( так для наглядности )
и что стало с твоим первым фреймворком полетел в топку? или дышит до сих пор? |
Автор: fesor 20.7.2009, 09:23 | ||
Ну я понял. Мой фреймворк? Ну я сделал на нем пару проектов и еще надо наверное будет сделать штуки 3... но наверное я его буду переписывать с нуля и попробую написать с его помощью небольшую CMS (мол Joomla c ТРУ MVC+ActiveRecord, блэкджеком и... девушками...) для нее например контроллер будет выглядеть примерно так:
Но я еще неуверен что это самый красивый вариант, а ничего красивее не придумалось( |
Автор: solenko 20.7.2009, 09:59 | ||
Да, передавая ссылку на Core во все классы, вы используете паттерн внедрения зависимости и не зависите от имени класса ) Вот только вы вводите зависимость от core + всех классов, от которых данный компонент действительно зависит. И просто "бери и подключай" не получится, т.к. класс c интерфейсом Core таки необходим. |
Автор: Sanchezzz 20.7.2009, 10:19 | ||
прочитай внимательнее я написал что у меня 2 типа классов которые не зависят от ядра и есть зависимые которые ваше подключаются через другую функцию и зачем мне подрубать компоненты независимо от ядра если они под мою систему заточены? на то они и кмпоненты.
со вторым типом классов да не получится и мне это не так сильно важно. а с первым все работает на ура. |
Автор: IZ@TOP 14.8.2009, 19:56 |
Мега тема! Особенно про "мега класс, который Сначез любит называть Core" - валялся) И про девушек с блэкджеком - супер) А вообще, автору респект. Интересные моменты уловил, для себя заметки сделал. P.S. А вы знаете чем больше всего полезны паттерны? Мы их все знаем и при необходимости изучения/дополнения чужого гоvнокода своим, вполне сможем его понять и с ним работать ![]() ![]() |