Шаблоны проектирования в PHP


Оглавление (нажмите, чтобы открыть):

Шаблоны проектирования в PHP

«Шаблоны проектирования» (или «паттерны») стали неотъемлемой частью современной разработки.

Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста. (Wiki)

Простым языком, определение звучит так: типовое решение для типовой задачи. Термин пришёл в программирование из архитектуры. В 1970-е годы архитектор Кристофер Александр составил набор шаблонов проектирования, типовых решений для различных архитектурных задач. Спустя полтора десятка лет эта идея была заимствована и адаптирована применительно к разработке графических оболочек языка SmallTalk. Сейчас паттерны встречаются повсеместно, постоянно изобретаются и переизобретаются. Некоторые из них описывают задачи, связанные с небольшим участком кода, другие определяют, например, способы работы в распределённых системах. Причём последние отвязаны от языка программирования. Интересный факт: некоторые шаблоны в языках появились вследствие ограничений самих языков и пытаются обойти их.

Как минимум один паттерн проектирования мы уже знаем по уроку «статические методы». Его называют Фабрика. Фабрика — функция, порождающая объекты, создание которых сложнее, чем просто передача данных в конструктор.

шаблоны проектирования — методы PHP Factory

Я читал о фабричных методах. Может кто-нибудь объяснить, почему предлагается размещать фабричные методы в отдельном фабричном классе?

Я хотел бы вместо этого написать это так:

Я неправ в этом?

Решение

Я бы не сказал, что вы «не правы», но есть «запах». Комбинируя фабричный метод в промышленном классе, архитектура нарушает некоторые из SOL > методические рекомендации:

  1. Единственная ответственность: класс теперь делает две вещи (когда он должен сделать одну).
  2. Принцип открытия / закрытия: класс открыт только наполовину (когда он должен быть полностью открыт).
  3. Сегрегация интерфейса: потребители класса используют собственные методы изготовленного объекта, без фабрики и наоборот (когда потребители должны зависеть только от необходимых методов)

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

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

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

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

Другие решения

Я подозреваю, что есть некоторые злоупотребления терминами Factory Method и Abstract Factory.

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

Абстрактная Фабрика предоставляет интерфейс для создания семейств связанных объектов. Мы используем его, когда хотим изолировать весь процесс создания от использования. Например, я использовал его, когда разрабатывал несколько пошаговых карточных игр. Все игры были разными и имели разную логику (разное количество игроков, разные выигрышные комбинации, порядок ходов, система очков и т. Д.). Но так как они были пошаговыми карточными играми, они были в то же время одинаковыми (от угла различий): вы ожидаете достаточного количества игроков (каждый объект игрока является разным для каждой игры), у вас есть дилер (еще один объект, который инкапсулирует правила и различия для каждой реализации), и вы запускаете игру, просто переключаясь между игроками, пока не произойдет какое-либо условие. Так что я смог использовать ту же игровую среду, которую я инициализировал с помощью реализации diff abstract factory.

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

Шаблоны

Большинство задач, с которыми часто приходится сталкиваться программистам, уже давным-давно решены другими членами нашего сообщества. Шаблоны проектирования как раз и являются тем средством, с помощью которого люди могут поделиться друг с другом накопленным опытом. Как только шаблон становится всеобщим достоянием, он обогащает наш язык и позволяет легко поделиться с другими новыми идеями проектирования и их результатами. С помощью шаблонов проектирования просто выделяют общие задачи, определяют проверенные решения и описывают вероятные результаты.

Что такое шаблоны проектирования

«В мире программного обеспечения, шаблон — это реальное проявление генетической памяти организации.»

— Гради Буч (Grady Booch), из книги Core J2EE Patterns

«Шаблон — это решение задачи в некотором контексте.»

— «Банда четырех» (The Gang of Four), из книги Design Patterns: Elements of Reusable Object-Oriented Software

Как следует из приведенных выше цитат, — это задача, взятая из практики передовых программистов, решение которой проанализировано и объяснено. Задачи имеют свойство повторяться, и веб-программистам приходится решать их снова и снова. Как обработать входящий запрос? Как преобразовать данные в команды для нашей системы? Как ввести данные? Как представить результаты? Со временем мы находим более или менее изящные ответы на эти вопросы и создаем неформальный набор методов, которые затем снова и снова используем в своих проектах. Эти методы — и есть шаблоны проектирования.

С помощью шаблонов проектирования описываются и формализуются типовые задачи и их решения. В результате опыт, который нарабатывается с большим трудом, становится доступным широкому сообществу программистов. Шаблоны должны быть построены, главным образом, по «восходящему», а не «нисходящему» принципу. Их корень — в практике, а не в теории. Но это совсем не означает, что в шаблонах проектирования отсутствует элемент теории. Шаблоны основаны на реальных методах, используемых реальными программистами. Знаменитый приверженец шаблонов Мартин Фаулер говорит, что он открывает шаблоны, а не создает их. Поэтому многие шаблоны будут вызывать у вас чувство «дежавю» — ведь вы будете узнавать методы, которые используете сами.

Каталог шаблонов — это не книга кулинарных рецептов. Рецептам можно следовать буквально, а код можно скопировать и вставить в проект с незначительными изменениями. Вам не всегда нужно даже понимать весь код, используемый в этом «рецепте». Шаблоны проектирования описывают подходы к решению конкретных задач. Детали реализации могут существенно меняться в зависимости от более широкого контекста. От этого контекста зависит выбор используемого языка программирования, природа приложения, размер проекта и специфика задачи.

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

Сначала синтаксический анализатор сканирует текст на предмет поиска триггерных лексем (trigger tokens). Когда он находит соответствие, то передает лексему другому объекту-анализатору, который специализируется на чтении содержимого, расположенного внутри тегов. В результате данные шаблона продолжаются анализироваться до тех пор, пока не произойдет синтаксическая ошибка, не будет достигнут их конец, либо не будет найдена другая триггерная лексема. В случае нахождения такой лексемы, объект-анализатор также должен передать ее на обработку соответствующей программе — скорее всего, анализатору аргументов. Все вместе эти компоненты образуют то, что называется рекурсивным нисходящим синтаксическим анализатором.

Итак, вот наши участники: MainParser, TagParser и ArgumentParser. Мы также определили класс ParserFactory, который создает и возвращает эти объекты. Но, конечно, все идет не так гладко, как хотелось бы, и позже, на одном из совещаний, вы узнаете, что в шаблонах нужно поддерживать несколько синтаксисов. И теперь вам нужно создать параллельный набор объектов-анализаторов в соответствии с синтаксисом: OtherTagParser, OtherArgumentParser и т.д.

Вам поставлена такая задача: нужно генерировать различные наборы объектов в зависимости от конкретной ситуации, и эти наборы объектов должны быть более или менее «прозрачными» для других компонентов системы. Случилось так, что «Банда четырех» в своей книге определила следующую задачу для шаблона Abstract Factory (Абстрактная фабрика): «Предусмотреть интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов». Этот шаблон нам прекрасно подходит! По сути нашей задачи мы как раз должны определить и очертить рамки использования данного шаблона.

Наименование шаблона уже само по себе очень ценно; таким образом создается что-то вроде общего словаря, который годами накапливается и используется в среде профессионалов. Такие условные обозначения помогают в совместных разработках, когда оцениваются и тестируются альтернативные подходы и их различные результаты. Например, при обсуждении семейств альтернативных синтаксических анализаторов вы можете просто сказать коллегам, что система создает каждый набор с помощью шаблона Abstract Factory. Они покивают с умным видом; кто-то сразу поймет, о чем идет речь, а кто-то отметит про себя, что нужно будет узнать об этом позже. Но суть в том, что у этого набора идей и различных результатов есть абстрактный описатель, который способствует созданию более кратких обозначений. Я проиллюстрирую это чуть позже.

И наконец, согласно международному законодательству, некорректно писать о шаблонах, не процитировав Кристофера Александера (Christopher Alexander), профессора архитектуры, работы которого оказали огромное влияние на первых сторонников объектно-ориентированных шаблонов. Вот что он пишет в книге A Pattern Language (Oxford University Press, 1977):

«Каждый шаблон описывает задачу, которая возникает снова и снова, а затем описывает суть решения данной задачи, так что вы можете использовать это решение миллион раз, каждый раз делая это по-разному.»

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

Мастер Йода рекомендует:  Проблемы с фтп-сервером

Обзор шаблонов проектирования

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

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

Формулировка задачи

Независимо от изящества решений (а некоторые из них действительно очень изящны), формулировка задачи и ее контекста — это основа шаблона. Сформулировать задачу гораздо труднее, чем применить какое-либо решение из каталога шаблонов. Это одна из причин того, что некоторые шаблоны могут применяться неправильно.

В шаблонах очень тщательно описываются условия задачи. Сначала кратко описывается сама задача, затем ее контекст; обычно приводится типичный пример и одна или несколько диаграмм. Анализируется специфика задачи, ее различные проявления. Также описываются все признаки, которые могут помочь при идентификации задачи.

Решение

Решение сначала кратко описывается вместе с задачей. Оно также описывается подробно, как правило, с использованием UML-класса и диаграмм взаимодействия. В шаблон обычно включается пример кода.

Я всегда сразу перехожу к разделу с кодом. Я считаю, что простой пример кода помогает разобраться в шаблоне. Этот пример часто сведен до минимума с целью демонстрации самой сути решения. Пример может быть написан на любом объектно-ориентированном языке, в нашем случае это PHP.

Но хотя код может присутствовать, в качестве решения никогда нельзя использовать метод «вырезать и вставить». Помните, что шаблон описывает подход к решению задачи, поскольку в реализации могут быть сотни нюансов. Представьте, что перед вами — инструкции о том, как сеять хлеб. Если вы просто слепо выполните все указания, то, скорее всего, будете голодать после сбора урожая. Гораздо полезнее подход, основанный на шаблоне, в котором описываются различные условия его применения. Основное решение задачи (заставить хлеб расти) всегда будет одним и тем же (посеять семена, поливать, собрать урожай), но реальные шаги, которые надо будет предпринять, зависят от всевозможных факторов, таких как тип почвы, местность, местные вредные насекомые и т.д.

Мартин Фаулер называет решения, описанные в шаблонах, «полусырыми», т.е. программист должен взять идею решения и закончить ее самостоятельно.

Результаты

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

Зачем используются шаблоны проектирования

Так в чем преимущества шаблонов? Учитывая, что шаблон — это поставленная задача и описанное решение, ответ, казалось бы, очевиден. Шаблоны помогают решать распространенные задачи. Но, конечно, шаблон — это нечто большее.

Сколько раз вы доходили до какого-то этапа в проекте и обнаруживали, что дальше идти некуда? Вполне вероятно, вам нужно вернуться немного назад, прежде чем начать снова. Определяя распространенные задачи, шаблоны помогают улучшить проект. И иногда первый шаг к решению — это осознание того, что есть проблема.

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

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

PHP и шаблоны проектирования

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

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

Например, для реализации шаблона Front Controller часто требуется довольно много времени. Хорошо, если инициализация имеет место один раз при запуске приложения, но гораздо хуже, если она происходит при каждом запросе. Это не значит, что нельзя использовать данный шаблон; я применял его в своей практике с очень хорошими результатами. Просто при обсуждении шаблона мы должны обязательно принять во внимание вопросы, связанные с PHP.

В PHP можно программировать, вообще не определяя никаких классов (хотя, учитывая продолжающееся развитие PEAR, вероятно, вы будете в некоторой степени оперировать объектами). Хотя здесь мы почти полностью концентрируемся на объектно-ориентированных решениях задач программирования, не считайте это залпом из всех орудий в войне между приверженцами разных стилей программирования. Шаблоны могут сосуществовать с другими, более традиционными, подходами. Убедительное доказательство тому — PEAR. В сборках PEAR очень изящно используются шаблоны проектирования. Они склонны быть объектно-ориентированными по своей природе. И это делает их более, а не менее, полезными в процедурных проектах. Поскольку сборки PEAR автономны и их сложность скрыта за четко описанным интерфейсом, их легко включить в проект любого типа.

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

Композиция и наследование

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

Проблема

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

На рисунке ниже приведен простой пример с использованием UML:

Родительский класс и два дочерних

Абстрактный класс Lesson моделирует занятие в колледже. Он определяет абстрактные методы cost() и chargeType(). На диаграмме показаны два реализующих класса, FixedPriceLesson и TimedPriceLesson, которые обеспечивают разные механизмы оплаты за занятия. С помощью этой схемы наследования я могу легко изменить реализацию занятия. Клиентский код будет знать только, что он имеет дело с объектом типа Lesson, поэтому детали механизма оплаты будут прозрачными.

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

На рисунке ниже показано решение проблемы «в лоб»:

Неудачная структура наследования

Здесь показана явно неудачная иерархия. Мы не сможем в дальнейшем использовать это дерево наследования, чтобы управлять механизмами оплаты, не дублируя большие блоки функциональности. Эти стратегии оплаты повторяются в семействах классов Lecture и Seminar.

На данном этапе мы должны рассмотреть использование условных операторов в суперклассе Lesson, чтобы избавиться от дублирования. В сущности, мы удаляем логику оплаты из дерева наследования вообще, перемещая ее вверх, в суперкласс. Это полная противоположность традиционному рефакторингу, когда условные операторы заменяются полиморфизмом. Вот как выглядит исправленный класс Lesson:

А вот как я должен работать с этими классами:

Диаграмма нового класса показана на рисунке ниже:

Иерархия наследования улучшена в результате удаления расчетов стоимости занятий из подклассов

Я сделал структуру класса намного более управляемой, но дорогой ценой. Использование условных операторов в данном коде — это шаг назад. Обычно мы стараемся заменить условный оператор полиморфизмом. А здесь я сделал противоположное. Как видите, это вынудило меня продублировать условный оператор в методах chargeType() и cost(). Похоже, я обречен на дублирование кода.

Использование композиции

Для решения данной проблемы я могу воспользоваться шаблоном Strategy. Этот шаблон используется для перемещения набора алгоритмов в отдельный тип. Перемещая код для вычисления стоимости, я могу упростить тип Lesson:

Перемещение алгоритмов в отдельный тип

Я создал еще один абстрактный класс CostStrategy, в котором определены абстрактные методы cost() и chargeType(). Методу cost() нужно передать экземпляр класса Lesson, который он будет использовать для расчета стоимости занятия. Мы обеспечиваем две реализации класса CostStrategy. Объекты Lesson работают только с типом CostStrategy, а не с конкретной реализацией, поэтому мы в любое время можем добавить новые алгоритмы расчета стоимости, создавая подклассы на основе CostStrategy. При этом не понадобится вносить вообще никаких изменений в классы Lesson.

Приведем упрощенную версию нового класса Lesson:

Конструктору класса Lesson передается объект типа CostStrategy, который он сохраняет в виде свойства. Метод Lesson::cost() просто вызывает CostStrategy::cost(). Точно так же Lesson::chargeType() вызывает CostStrategy::chargeType(). Такой явный вызов метода другого объекта для выполнения запроса называется делегированием. В нашем примере объект типа CostStrategy — делегат класса Lesson. Класс Lesson снимает с себя ответственность за расчет стоимости занятия и возлагает эту задачу на реализацию класса CostStrategy. Вот как осуществляется делегирование:

Ниже приведено определение класса CostStrategy вместе с реализующими его дочерними классами:

Во время выполнения программы я легко могу изменить способ расчета стоимости занятий, выполняемый любым объектом типа Lesson, передав ему другой объект типа CostStrategy. Этот подход способствует созданию очень гибкого кода. Вместо того чтобы статично встраивать функциональность в структуры кода, я могу комбинировать объекты и менять их сочетания динамически:

В результате на выходе получим следующее:

Использование композиции при проектировании классов

Как видите, одно из следствий принятия этой структуры состоит в том, что мы рассредоточили обязанности наших классов. Объекты CostStrategy ответственны только за расчет стоимости занятия, а объекты Lesson управляют данными занятия.

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

Разделение

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

Проблема

Повторное использование — одна из основных целей объектно-ориентированного проектирования, и тесная связь — враг этой цели. Тесная связь имеет место, когда изменение в одном компоненте системы ведет к необходимости вносить множество изменений повсюду. Вы должны стремиться создавать независимые компоненты, чтобы можно было вносить изменения, не опасаясь «эффекта домино» непредвиденных последствий. Когда вы меняете компонент, степень его независимости влияет на вероятность того, что эти изменения вызовут ошибки в других частях системы.

Мастер Йода рекомендует:  Стартап в сфере искусственного интеллекта в 2020

На второй диаграмме в этой выше мы видели пример тесной связи. Поскольку логика схем оплаты повторяется в типах Lecture и Seminar, изменения в компоненте TimedPriceLecture приведут к необходимости внесения параллельных изменений в ту же логику в компоненте TimedPriceSeminar. Обновляя один класс и не обновляя другой, я нарушаю работу системы. При этом от интерпретатора PHP я не получу никакого предупреждения. Мое первое решение, с использованием условного оператора, породило аналогичную зависимость между методами cost() и chargeType().

Применяя шаблон Strategy, я преобразовал алгоритмы оплаты в тип CostStrategy, разместил их за общим интерфейсом и реализовал каждый из них только один раз.

Тесная связь другого рода может иметь место, когда много классов в системе внедрены явным образом в платформу или среду. Предположим, вы создаете систему, которая, например, работает с базой данных MySQL. Для запросов к серверу базы данных вы можете использовать такие функции, как mysqli_connect() и mysqli_query().

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

И проблема здесь не в зависимости системы от внешней платформы. Такая зависимость неизбежна, поскольку мы должны работать с кодом, который связывается с базой данных. Проблема возникает, когда такой код разбросан по всему проекту. Коммуникации с базами данных — это не главная обязанность большинства классов в системе, поэтому наилучшая стратегия — выделить такой код и сгруппировать его за общим интерфейсом. Таким образом вы будете способствовать независимости классов.

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

В PEAR эта проблема решена с помощью сборки PEAR::MDB2 (который пришел на смену сборке PEAR::DB). Это обеспечивает одну точку доступа для разных систем баз данных. А недавно, благодаря встроенному расширению PDO, эта модель была включена в сам язык PHP.

В классе MDB2 существует статический метод connect(), которому передается строка, содержащая имя источника данных (DSN). В зависимости от состава этой строки, метод возвращает экземпляр объекта, реализующего класс MDB2_Driver_Common. Поэтому для строки «mysql://» метод connect() возвращает объект типа MDB2_Driver_mysql, в то время как для строки, которая начинается с «sqlite://», он возвращает объект типа MDB2_Driver_sqlite. Структура этих классов показана на рисунке:

В сборке PEAR::MDB2 клиентский код отделен от объектов базы данных

Кроме того, сборка PEAR::MDB2 позволяет отделить код приложения от специфики платформы базы данных. При условии, что вы используете совместимый SQL-код, ваше приложение будет работать со многими СУБД, включая MySQL, SQLite, MSSQL и др. При этом вам не нужно будет вносить изменения в свой код, кроме, конечно, настройки DSN. Пожалуй, это единственное место в программе, где необходимо сконфигурировать контекст базы данных. На самом деле сборка PEAR::MDB2 до некоторой степени помогает также работать и с различными «диалектами» SQL — и это одна из причин, по которой вы можете решить использовать его, несмотря на скорость и удобство PDO.

Структура, показанная на рисунке выше имеет некоторое сходство с шаблоном Abstract Factory. Более простой по своей природе, он, тем не менее, имеет такое же назначение: генерировать объект, который реализует абстрактный интерфейс и не требует от клиента непосредственного создания экземпляра объекта.

Несмотря на то что в сборке MDB2 или в расширении PDO клиентский код отделен от специфики реализации платформы СУБД, свою часть работы вы все равно должны сделать. Если ваш (теперь уже универсальный) SQL-код рассредоточен по всему проекту, вы вскоре обнаружите, что единственное изменение какого-либо аспекта проекта может повлечь за собой каскад изменений во многих местах кода. И здесь самым типичным примером было бы изменение структуры базы данных, при котором добавление дополнительного столбца в таблице может повлечь за собой изменение SQL-кода многих повторяющихся запросов к базе данных. Поэтому вам следует подумать о том, чтобы извлечь этот код и поместить его в одну сборку, тем самым отделив логику приложения от специфики реляционной базы данных.

Ослабление связи

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

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

Если вы в коде будете использовать явные ссылки на класс Mailer или Texter, то тогда ваша система становится наглухо привязана к конкретному типу рассылки уведомлений. Это примерно то же самое, как привязаться к конкретному типу СУБД, используя в коде вызовы ее специализированных функций API.

Ниже приведен фрагмент кода, в котором детали реализации конкретной системы уведомления скрыты от кода, который ее использует:

Здесь я создал класс RegistrationMgr, который является простым клиентским классом для классов типа Notifier. Несмотря на то что класс Notifier абстрактный, в нем реализован статический метод getNotifier(), который создает и возвращает конкретный объект типа Notifier (TextNotifier или MailNotifier) в зависимости от сложившихся условий. В реальном проекте выбор конкретного класса типа Notifier должен определяться каким-нибудь гибким способом, например параметром в файле конфигурации. Здесь же я немного сжульничал и выбрал тип объекта случайным образом. Метод inform() классов MailNotifier и TextNotifier практически ничего не делает. Он просто выводит информацию, полученную в качестве параметра, а также дополняет ее типом сообщения, чтобы было видно, к какому классу он относится.

Обратите внимание на то, что только в методе Notifier::getNotifier() сосредоточена информация о том, какой конкретно объект типа Notifier должен быть создан. В результате я могу посылать уведомления из сотен разных участков программы, а при изменении типа уведомлений мне потребуется внести изменения только в один метод в классе Notifier.

Ниже приведен пример кода, вызывающий метод register() класса RegistrationMgr:

Вот что будет выведено в результате:

Пример использования ослабления связи между классами

На рисунке ниже приведена диаграмма классов нашего проекта:

В классе Notifier клиентский код отделен от кода реализации различных уведомителей

Обратите внимание на то, что эта структура очень сильно напоминает ту, которая используется в сборке PEAR::MDB2.

Проблемы применения шаблонов

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

Методология экстремального программирования (extreme Programming, XP) предлагает ряд принципов, которые применимы в данной ситуации. Первый принцип: «Вам необязательно это нужно» (обычно для него используют аббревиатуру YAGNI от You aren’t going to need it). Как правило, данный принцип применяется для функций приложения, но он имеет смысл и для шаблонов.

Когда я создаю большие проекты на PHP, то обычно разбиваю приложение на уровни, отделяя логику приложения от представления данных и персистентных уровней. Я использую всевозможные виды основных и промышленных шаблонов, а также их комбинации.

Но если меня попросят создать простую форму для обратной связи небольшого веб-сайта, я могу запросто использовать процедурный код, поместив его на одну страницу с HTML-кодом. В этом случае мне не нужна гибкость в огромных масштабах, потому что я не буду что-либо строить на этой первоначальной основе. Мне не нужно использовать шаблоны, которые решают соответствующие задачи в более широких системах. Поэтому я применяю второй принцип экстремального программирования: «Сделайте самый простой работающий вариант». Работая с каталогом шаблонов, думайте о структуре и процессе решения, обращая внимание на пример кода. Но прежде чем применить шаблон, подумайте о том, «когда его использовать», найдите соответствующий раздел и прочитайте о результатах применения шаблона. В некоторых контекстах лечение может оказаться хуже болезни.

Шаблоны проектирования: Простая Фабрика

Что вам приходит в голову, когда вы слышите слово “фабрика”? Для меня это место производства каких-то вещей — это действительно так. Стоит добавить, что произведённая продукция зависит от заказа.

К примеру вы заказали себе машину. Фабрика сконструирует её согласно внутренним правилам и спецификациям.

По аналогии, в мире программирования шаблон проектирования фабрика используется для создания объектов по заданным параметрам.

  1. Простая Фабрика. Позволяет создавать объекты без логики их создания в клиенте.
  2. Фабричный метод. Позволяет подклассам определять какой класс инициализировать.
  3. Абстрактная фабрика. В отличии от предыдущих методов, абстрактная фабрика позволяет создавать связные объекты.

Проблема

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

Со временем в процессе создания объекта Car нужно будет вводить некоторые изменения. К примеру создавать объект автомобиля нужно согласно определённым типам. Таким образом нам нужно будет менять код везде, где до тех пор создавался объект класса Car.

Через некоторое время ситуация может измениться ещё раз и ещё раз.

Чтобы не менять код проекта раз за разом целесообразно создать отдельный класс по шаблону Фабрика.

Решение

Теперь давайте создадим функционал по которому объект автомобиля будет создаваться по ключевому слову. Далее это позволит нам без проблем создавать автомобили по их типу: Sedan, SUV и так далее.

Реализуем шаблон проектирования Простая Фабрика. Обратите внимание на фабричный метод build.

Итак, у нас есть один абстрактный метод, который создаёт объекты других классов согласно типу автомобиля. Теперь нам необходимо создать классы самих автомобилей:

Теперь можем создавать автомобили:

Добавляем новый класс

Теперь добавить новый тип автомобиля проще простого. Пример:

Вывод

Шаблон проектирования Простая Фабрика позволяет нам создать централизованное место создания объектов схожего типа.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: https://code.tutsplus.com/tutorials/design-patterns-the-simple-factory-pattern—cms-22345
Перевел: Станислав Протасевич
Урок создан: 26 Апреля 2015
Просмотров: 7784
Правила перепечатки

5 последних уроков рубрики «PHP»

Фильтрация данных с помощью zend-filter

Когда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных.

Мастер Йода рекомендует:  Транзакции в СУБД MySQL

Контекстное экранирование с помощью zend-escaper

Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

Подключение Zend модулей к Expressive

Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.

Совет: отправка информации в Google Analytics через API

Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.

Подборка PHP песочниц

Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

Шаблон проектирования factory php

Шаблон проектирования factory php ещё называют фабричный метод.

Он позволяет создавать объекты во время работы нашего веб приложения.

То есть этот шаблон называют Фабрикой потому что он отвечает за производством объектов.

Давайте посмотрим на примере как можно его использовать:

Сама идея этого шаблона: чтобы создать объект некоторого класса нам необходимо обратиться к классу MyFactory (например) у этого класса обращаемся к статическому методу factory (например) и передаём ему имя класса объект которого Мы хотим получить, далее уже пишем все по нашему усмотрению.

Что-то похожее на автоматическую загрузку классов только тут ещё и объект создаётся.

Данный шаблон используется в различных фреймворках на PHP, и в Joomla (новой версий).

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

Вот ещё один классический пример использования шаблона factory:

Серия: Шаблоны проектирования в php.

Шаблон проектирования Одиночка (Singleton pattern).

    564

    • php
    • php design patterns

    воскресенье 16-июня-2020 в 13:04:52

Шаблон проектирования Репозиторий (Repository Pattern).

  • Категория :Шаблоны проектирования (паттерны) в php
  • Из серии:Шаблоны проектирования в php.

Разделы блога

Ключевые слова

Самое популярное

Как передать переменную во view или layout в yii2.

Формирование запросов для выборки из БД в yii2

Добавления кода javascript и css в файлах видов.

Как включить или отключить debug панель yii2 на хостинге.

Сервисы и репозитории в yii2.

Как в yii2 отключить bootstrap и jquery или заменить на свой.

Как создать свою кнопку в GridView yii2.

Создание POST ссылки в yii2.

© 2020 Serg Coderius — разработка сайтов и сервисов.

Использование материалов возможно лишь при наличии активной ссылки на источник.

Пример шаблона проектирования MVC в PHP

Шаблон проектирования MVC представляет собой шаблон программной архитектуры «Модель-Представление-Контроллер». MVC — это один из способов организации кода и набор рекомендаций, описывающих ряд правил. Задача MVC заключается в том, чтобы наш код проще воспринимался и читался, а также быстрее обрабатывался компьютером.

Главная идея MVC — отделить внешний вид приложения от его логики. Здесь мы говорим о веб-приложениях, поэтому внешний вид в данном случае — это HTML-разметка и CSS-оформление. А вот под логикой понимаются разнообразные скрипты, классы, функции и пр.

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

Основные составляющие MVC

Как уже было сказано выше, главными составляющими шаблона проектирования MVC являются 3 части: Контроллер, Представление и Модель. Визуально это выглядит следующим образом:

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

Модель

«Модель» — это постоянное хранилище данных, которое используется по всей структуре. Компонент обеспечивает доступ к данным для просмотра, отбора, записи. По сути это мост между другими компонентами: «Контроллер» и «Представление».

«Модель» не «знает», что происходит с данными дальше. Её задачи — поиск и подготовка данных, передаваемых прочим компонентам MVC, а также обработка их в постоянном хранилище. По сути это «привратник», который стоит возле хранилища данных и не задаёт лишних вопросов, однако принимает все запросы, которые поступают. «Модель» — вершина структуры MVC, без неё невозможна связь между «Представлением» и «Контроллером».

Представление

Это часть системы, в которой данным, запрашиваемым у «Модели», задаётся финишный вид их вывода. В веб-приложениях, построенных с помощью MVC, «Представлением» называют компонент, где генерируется и отображается код HTML.

Кроме того, «Представление» перехватывает действия пользователей, которые после передаются «Контроллеру». Характерный пример — кнопка, генерируемая «Представлением». Если пользователь её нажимает, в «Контроллере» запускается действие.

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

Контроллер

Он занимается обработкой данных, которые вводятся пользователем, а также участвует в обновлении «Модели». Этот компонент работает при взаимодействии с пользователем.

«Контроллер» можно назвать сборщиком информации, которая передаётся в «Модель» с последующей организацией хранения. Компонент не содержит другой логики, кроме той, что нужна для сбора входящих данных. Также он подключается лишь к одному «Представлению» и к одной «Модели». Таким образом создаётся система с 1-сторонним потоком данных, с одним выходом и одним входом в точках обмена.

Компонент получает задачи с целью выполнения лишь тогда, когда пользователь осуществляет взаимодействие с «Представлением». При этом не стоит путать «Контроллер» со шлюзом, присваивая ему задачи и функции, относящиеся к «Представлению». Распространённая ошибка — наделение «Контроллера» функциями, отвечающими только за передачу и обработку данных из компонента «Модель» в компонент «Представление».

MVC в PHP

Давайте напишем приложение на PHP с архитектурой MVC. Начнём с примера каркаса:

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

В нашем примере PHP MVC отсутствует специфический функционал для контроллера, а всё потому, что в веб-приложении взаимодействия пользователя не определены. Что касается представления, то этот компонент содержит весь функционал, ведь пример предназначен только для демонстрации.

Теперь расширим PHP-пример и покажем, как будем добавлять функционал контроллера, добавляя в приложение взаимодействия:

Итак, PHP-пример расширен базовым функционалом. А теперь посмотрим, как будет выглядеть настройка взаимодействий между компонентами:

Запустив код, при нажатии на ссылку вы увидите строку, предназначенную для изменения данных.

Таким образом, можно сказать, что мы ознакомились с базовой теорией модели MVC, реализовав с его помощью простого PHP-приложения.

Материал подготовлен специально для OTUS на основе статьи «The MVC Pattern and PHP. Part 1».

Паттерны (шаблоны) проектирование в PHP. Введение.

Паттерн проектирования (design pattern) — это общее типовое решение некоторой проблемы, многократно повторяемое в процессе проектирования архитектуры программного продукта. Они показывают отношения и взаимодействия между классами, позволяют сделать систему гибкой и легко изменяемой. За счет их правильного использования повышается коэффициент использования готовых решений.

Я буду постепенно описывать каждый паттерн и добавлять его в список на этой странице.

Я предполагаю, что читатель осведомлен о понятиях ООП и знает в чем разница между интерфейсом и абстрактным классом. Знает что такое полиморфизм, инкапсуляция и наследование. Если вы не уверены в своих силах, рекомендую к прочтению статью из википедии «Объектно-ориентированное программирование».

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

htmllab

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

Шаблоны (иногда их называют паттернами) обычно не привязаны к языку программирования, потому что предназначены для решения типовой задачи. Узнав как создается шаблон Адаптер или Декоратор вы можете попробовать его реализовать на языке объектно-ориентированном языке программирования, который знаете. Знание базовых шаблонов также помогает разработчикам общаться в команде с коллегами — не тратиться доп. время на разъяснение концепции решения той или иной задачи.

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

Шаблоны проектирования: книги

  1. PHP. Объекты, шаблоны и методики программирования. Мэтт Зандстра
  2. Паттерны проектирования. Эрик Фримен, Элизабет Фримен
  3. Приемы объектно-ориентированного проектирования. Паттерны проектирования. Эрих Гамма, Ричард Хелм и др.
  4. Шаблоны реализации корпоративных приложений. Кент Бек
  5. Применение UML 2.0 и шаблонов проектирования. Введение в объектно-ориентированный анализ, проектирование и итеративную разработку. Крэг Ларман

Книга Зандстры выдержала несколько изданий, достаточно простым языком разъясняет сложные пример (кому вначале покажется сложным, посмотрите последние две книги из списка �� ). Кроме шаблонов проектирования рассматриваются хорошие практики разработки программного обеспечения и все это применимо к PHP.

Книга Фрименов, как и все книги из серии Head&First призваны навести порядок в голове у всех начинающих, в данном случае — работать с шаблонами проектирования. Содержит очень много примеров на Java (если знаком PHP — можно читать).

На книгу Бека часто ссылается Зандстра. Но «Шаблоны реализации корпоративных приложений» лучше читать после просмотра других книг списка.

Шаблоны проектирования: фреймворки

Для того, чтобы хорошо разобраться в шаблонах, нужно знакомиться с кодом передовых разработок. Например, знакомимся с шаблоном Наблюдатель (Observer). Если параллельно рассматриваем Zend Framework 2, то обнаруживаем компонент EventManager, который реализует шаблон Наблюдатель. Это касается всех передовых фреймворков, которые на слуху.

Примечание: перед началом знакомства с шаблонами, можно посмотреть язык визуального моделирования UML. UML даёт возможность читать и создавать диаграммы, описывающие шаблоны проектирования (на самом деле, для описания взаимодействия между объектами, состояний объектов используются разные комбинации базовых фигур UML). Если есть желание создать свою UML-диаграмму не на листке бумаги, можно воспользоваться одним из онлайн-сервисов или стационарной программой. Обратите внимание на DIA она медленно, но уверенно развивается. У неё есть и неудобные моменты с точки зрения удобства использования, но она меня очень выручила, когда однажды искал способ автоматической генерации PHP-кода из диаграммы (есть плагин).

Шаблоны проектирования в PHP : 1 комментарий

Добавить комментарий Отменить ответ

Для отправки комментария вам необходимо авторизоваться.

Пример — Шаблон проектирования Строитель на PHP

В шаблоне Builder управляющий и строитель работают вместе для создания объекта. Управляющий контролирует задание и указывает, какие части и вариации войдут в объект. Строитель знает, как собрать объект, указанный в спецификации.

В этом примере у нас есть управляющий, HTMLPageDirector, которому дается строитель HTMLPageBuilder. Управляющий говорит строителю, что будет с типом pageTitle, каким будет pageHeading, и дает несколько строк текста для страницы. Затем у управляющего есть строитель, который делает окончательную сборку деталей и возвращает страницу.

Вывод

Рекомендуем хостинг TIMEWEB

Рекомендуемые статьи по этой тематике

Добавить комментарий