Как вы организуете и управляете вспомогательными объектами, такими как ядро базы данных, уведомления пользователей, обработка ошибок и т.д. в объектно-ориентированном проекте на основе PHP?
Скажем, у меня большая PHP CMS. CMS состоит из различных классов. Несколько примеров:
- объект базы данных
- Управление пользователями
- API для создания / изменения / удаления элементов
- объект обмена сообщениями для отображения сообщений конечному пользователю
- обработчик контекста, который перенесет вас на нужную страницу
- класс панели навигации, который показывает кнопки
- объект регистрации
- возможно, настраиваемая обработка ошибок
и т.п.
Я имею дело с вечным вопросом, как лучше всего сделать эти объекты доступными для каждой части системы, которая в этом нуждается.
мой первый подход много лет назад заключался в том, чтобы иметь глобальный объект $ application, содержащий инициализированные экземпляры этих классов.
global $application;
$application->messageHandler->addMessage("Item successfully inserted");
Затем я переключился на шаблон Singleton и фабричную функцию:
$mh =&factory("messageHandler");
$mh->addMessage("Item successfully inserted");
но меня это тоже не устраивает. Модульные тесты и инкапсуляция становятся для меня все более и более важными, и, в моем понимании, логика глобальных / одиночных объектов разрушает основную идею ООП.
Тогда, конечно, есть возможность дать каждому объекту ряд указателей на вспомогательные объекты, которые ему нужны, вероятно, самый чистый, ресурсосберегающий и удобный для тестирования способ, но я сомневаюсь в ремонтопригодности этого в долгосрочной перспективе.
Большинство фреймворков PHP, которые я изучал, используют либо шаблон singleton, либо функции, которые обращаются к инициализированным объектам. Оба хороших подхода, но, как я уже сказал, я не доволен ни одним.
Я хотел бы расширить свой кругозор в отношении того, какие общие закономерности существуют здесь. Я ищу примеры, дополнительные идеи и указатели по направлению к ресурсам , которые обсуждают это с долгосрочной , в реальном мире точки зрения.
Кроме того, мне интересно услышать о специализированных, нишевых или просто странных подходах к этому вопросу.
источник
$mh=&factory("messageHandler");
бессмысленно и не дает никакого преимущества в производительности. Кроме того, это устарело в версии 5.3.Ответы:
Я бы избегал подхода Singleton, предложенного Флавием. Есть множество причин избегать этого подхода. Это нарушает хорошие принципы ООП. В блоге о тестировании Google есть несколько хороших статей о синглтоне и о том, как его избежать:
http://googletesting.blogspot.com/2008/08/by-miko-hevery-so-you-join-new-project.html http://googletesting.blogspot.com/2008/05/tott-using-dependancy -injection-to.html http://googletesting.blogspot.com/2008/08/where-have-all-singletons-gone.html
альтернативы
поставщик услуг
http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html
внедрение зависимости
http://en.wikipedia.org/wiki/Dependency_injection
и объяснение php:
http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection
Это хорошая статья об этих альтернативах:
http://martinfowler.com/articles/injection.html
Реализация внедрения зависимостей (DI):
Я считаю, что вы должны спросить, что нужно в конструкторе для функционирования объекта :
new YourObject($dependencyA, $dependencyB);
Вы можете указать необходимые объекты (зависимости) вручную (
$application = new Application(new MessageHandler()
). Но вы также можете использовать структуру DI (на странице википедии есть ссылки на инфраструктуры PHP DI ).Важно то, что вы передаете только то, что вы действительно используете (вызываете действие), а НЕ то, что вы просто передаете другим объектам, потому что им это нужно. Вот недавний пост от «дяди Боба» (Роберт Мартин), в котором обсуждается ручной DI и использование фреймворка .
Еще несколько мыслей о решении Флавия. Я не хочу, чтобы этот пост был антипостом, но я думаю, что важно понять, почему внедрение зависимостей, по крайней мере, для меня, лучше, чем глобальные.
Хотя это не «настоящая» реализация синглтона , я все же думаю, что Флавиус ошибся. Глобальное состояние плохое . Обратите внимание, что в таких решениях также используются сложные для тестирования статические методы .
Я знаю, что многие люди это делают, одобряют и используют. Но, читая статьи в блоге Misko Heverys (эксперт по тестированию Google ), перечитывание их и медленное переваривание его дизайн.
Если вы хотите иметь возможность протестировать свое приложение, вам необходимо применить другой подход к его разработке. Когда вы занимаетесь тестовым программированием, у вас будут трудности с такими вещами: «Далее я хочу реализовать регистрацию в этом фрагменте кода; давайте сначала напишем тест, который регистрирует базовое сообщение, а затем придумаем тест, который заставит вас написать и использовать глобальный регистратор, который нельзя заменить.
Я все еще борюсь со всей информацией, полученной из этого блога, и ее не всегда легко реализовать, и у меня много вопросов. Но у меня нет возможности вернуться к тому, что я делал раньше (да, глобальное состояние и синглтоны (большая S)) после того, как я понял, что говорил Миско Хевери :-)
источник
Я бы так и поступил. Создает объект по запросу:
Я так делаю, он уважает принципы ООП, это меньше кода, чем то, что вы делаете прямо сейчас, и объект создается только тогда, когда он нужен коду впервые.
Заметка : то, что я представил, даже не является настоящим одноэлементным шаблоном. Синглтон допускает только один экземпляр самого себя, определяя конструктор (Foo :: __ constructor ()) как закрытый. Это только «глобальная» переменная, доступная для всех экземпляров «Приложения». Вот почему я считаю его использование правильным, поскольку оно НЕ игнорирует хорошие принципы ООП. Конечно, как и все на свете, этим «шаблоном» тоже не стоит злоупотреблять!
Я видел, как это используется во многих фреймворках PHP, в том числе в Zend Framework и Yii. И вы должны использовать фреймворк. Я не скажу вам, какой именно.
Дополнение Для тех из вас, кто беспокоится о TDD , вы все равно можете сделать некоторую проводку для внедрения зависимостей. Это могло выглядеть так:
Здесь достаточно возможностей для улучшения. Это просто PoC, используйте свое воображение.
Почему это так? Что ж, в большинстве случаев приложение не будет проходить модульное тестирование, оно будет запускаться, надеюсь, в производственной среде . Сила PHP - в его скорости. PHP НЕ является и никогда не будет «чистым языком ООП», как Java.
В приложении есть только один класс Application и только один экземпляр каждого из его помощников, самое большее (согласно ленивой загрузке, как указано выше). Конечно, синглтоны - это плохо, но опять же, только если они не соответствуют реальному миру. В моем примере это так.
Стереотипные «правила» типа «одиночки - это плохо» являются источником зла, они предназначены для ленивых людей, не желающих думать самостоятельно.
Да, я знаю, манифест PHP ПЛОХО, технически говоря. И все же это успешный язык в своем хакерском стиле.
добавление
Единый функциональный стиль:
источник
Мне нравится концепция внедрения зависимостей:
Фабьен Потенсье написал действительно хорошую серию статей о внедрении зависимостей и необходимости их использования. Он также предлагает красивый и небольшой контейнер для внедрения зависимостей под названием Pimple, который мне очень нравится использовать (подробнее на github ).
Как было сказано выше, мне не нравится использование синглтонов. Хорошее резюме того, почему синглтоны не являются хорошим дизайном, можно найти здесь, в блоге Стива Йегге .
источник
decupling from GOD object
: stackoverflow.com/questions/1580210/… с очень хорошим примеромЛучше всего иметь какой-то контейнер для этих ресурсов. Некоторые из наиболее распространенных способов реализации этого контейнера :
одиночка
Не рекомендуется, потому что это сложно проверить и подразумевает глобальное состояние. (Singletonitis)
реестр
Устраняет синглтонит, ошибку Я бы тоже не рекомендовал реестр, потому что это тоже своего рода синглтон. (Трудно модульное тестирование)
наследование
К сожалению, в PHP нет множественного наследования, поэтому это ограничивает все цепочкой.
Внедрение зависимости
Это лучший подход, но тема более важная.
традиционный
Самый простой способ сделать это - использовать инъекцию конструктора или установщика (передать объект зависимости с помощью установщика или в конструкторе класса).
Каркасы
Вы можете использовать свой собственный инжектор зависимостей или использовать некоторые из фреймворков для внедрения зависимостей, например. Yadif
Ресурс приложения
Вы можете инициализировать каждый из своих ресурсов в начальной загрузке приложения (которая действует как контейнер) и получать к ним доступ в любом месте приложения, обращаясь к объекту начальной загрузки.
Такой подход реализован в Zend Framework 1.x
Загрузчик ресурсов
Вид статического объекта, который загружает (создает) необходимый ресурс только при необходимости. Это очень умный подход. Вы можете увидеть это в действии, например, при реализации компонента Symfony Dependency Injection.
Инъекция в определенный слой
Ресурсы не всегда нужны где-либо в приложении. Иногда они просто нужны, например, в контроллерах (MV C ). Тогда вы можете закачивать ресурсы только туда.
Обычный подход к этому - использование комментариев к блоку документов для добавления метаданных внедрения.
Посмотрите мой подход к этому здесь:
Как использовать внедрение зависимостей в Zend Framework? - Переполнение стека
Напоследок хотелось бы добавить здесь заметку об очень важной вещи - кешировании.
В общем, независимо от выбранной вами техники, вы должны подумать, как будут кэшироваться ресурсы. Кеш будет самим ресурсом.
Приложения могут быть очень большими, и загрузка всех ресурсов по каждому запросу очень дорога. Есть много подходов, включая этот appserver-in-php - Project Hosting on Google Code .
источник
Если вы хотите сделать объекты глобально доступными, вам может быть интересен шаблон реестра . Для вдохновения взгляните на Zend Registry .
Так же и вопрос о реестре и синглтоне .
источник
Объекты в PHP занимают много памяти, как вы, вероятно, видели из своих модульных тестов. Поэтому идеально как можно скорее уничтожить ненужные объекты, чтобы сэкономить память для других процессов. Имея это в виду, я считаю, что каждый объект соответствует одной из двух форм.
1) Объект может иметь много полезных методов или его нужно вызывать более одного раза, и в этом случае я реализую синглтон / реестр:
2) Объект существует только в течение жизни вызывающего его метода / функции, и в этом случае простое создание полезно для предотвращения слишком долгого сохранения объектов в живых ссылках на объекты.
Хранение временных объектов ВЕЗДЕ может привести к утечкам памяти, поскольку ссылки на них могут быть забыты о хранении объекта в памяти для остальной части скрипта.
источник
Я бы выбрал функцию, возвращающую инициализированные объекты:
В среде тестирования вы можете определить его для возврата макетов. Вы даже можете определить внутри, кто вызывает функцию, используя debug_backtrace (), и вернуть разные объекты. Вы можете зарегистрироваться в нем, кто хочет получить какие объекты, чтобы получить представление о том, что на самом деле происходит внутри вашей программы.
источник
Почему бы не прочитать прекрасное руководство?
http://php.net/manual/en/language.oop5.autoload.php
источник