Что такое паттерны в программировании. Паттерны проектирования и их представление в нотации UML. Шаблоны параллельного программирования

Половина второго ночи, несколько дней недосыпа... И вдруг озарение - наконец-то я понял, какой-такой супер-пупер скрипт мне нужен для раскрутки моего нового сайта. Сказано - сделано, сажусь... нет не писать этот супер-пупер скрипт:) Программисты народ ленивый, спецы по раскрутке тем более (отсюда и баннерообменные сети, авторегистраторы в каталогах и пр. лентяйские штучки). В общем лезу на Google и начинаю методично обшаривать Рунет в поисках требуемого. Проходит час, два... В душу закрадываются смутные подозрения, плавно перерастающие в уверенность, что требуемый мне скрипт в природе либо не существует, либо спрятан на чьем-то заповедном локалхосте с правами доступа 000.

Разбиваю стоящую перед скриптом задачу на несколько составных и минут за 15 пишу реализацию большинства его (скрипта) функций:) Радостно берусь за следующую фичу... В общем, необходимо всего-то навсего обеспечить постраничный вывод контента. То есть, например, имеется 60 статей и нужно эти статьи выводить по 10 на страницу и построить меню для оного вывода в виде "1 2 3 ..."

Четыре утра, окромя заветного "LIMIT" в голову ничего не приходит. И дело даже не в том, что БД к моему скрипту никакого отношения не имеет. С горечью понимаю, что квалификация потеряна, лучшие годы жизни прожиты впустую и... иду на Google ;)
После... в общем уже наступил рассвет. К этому времени я познакомился с кучей литературы по данному вопросу, но везде ситуация так или иначе рассматривалась в контексте работы с SQL, либо я постоянно наталкивался на какую-то растиражированно-заумную статью про "отделение алгоритма постраничного вывода от контекста или-что-то-в-этом-роде" (ИМХО, нижеприведенные 7 строчек кода эту проблему решают) в которой ничегошеньки не понял... :(И вот когда я уже отчаялся и решил что утро вечера мудренее, на каком-то дремучем, заросшим мхом форуме (адрес к сожалению канул в Лету - по крайней мере у меня его нет), наткнулся на переписку некоего вечно извиняюшегося Программера и безумного и злобного Модератора - тема переписки была посвящена именно построению меню для постраничного вывода. И это было именно то что нужно!

В общем, ниже по тексту я исправляю несправедливость: теперь в Интернете есть статья о том, как обеспечить построение меню для постраничного вывода! Простой рабочий код на PHP, с подробными комментариями. Никакой излишней функциональности типа ссылок "next page". Сделано сие намеренно - ни разу в жизни не нажимал на "next" если можно было нажать на номер страницы - а данная статья написана с целью дать возможность понять основной принцип, т.е. чем меньше кода, тем лучше (хотя кнопка "next" и добавляется тремя строками кода).

Соглашения о комментариях к коду:

  • "Статья" - это некий целостный блок данных (не обязательно текстовых) который и выводится по несколько штук на страницу (это могут быть статьи, новости, картинки и пр.)
  • "Страница" - это страница:) на которой расположено некоторое количество "статей".

Таким образом, на экране браузера мы получили что-то вроде "1 2 3 4 5 6 7 8", при этом URL (на моем компьютере, при выбранной третьей "странице") выглядит так: http://localhost/ep/ep.php?from=20 . Обратите внимание, на то, что странице под номером "3" соответствует значение from=20 то есть отсчет выводимых статей будет вестись в данном случае с 20-й.

Вообще, как вы уже поняли, переменная $from играет здесь ключевую роль. Она участвует в построении ссылок на каждую отдельную страницу (если, конечно, можно так выразиться:), и самое главное - дает нам точку отсчета для вывода статей. Например (и наверняка вы сделаете именно так) указатели на статьи (или сами статьи;) можно поместить в массив, с последующей выборкой и выводом в браузер по 10 статей, начиная от значения $from.

То есть еще раз иными словами: весь огород собственно и городился ради того, чтобы дать скрипту данные относительно того на какой "странице" сейчас находится посетитель и начиная с какого номера "статьи" следует вывести заданное количество этих статей на страницу - а это и есть значение переменной $from. Т.е. при $from=20 выведутся страницы с 21 по 30 (это также зависит от реализации вывода и значения переменной $articles_per_page)

Последнее обновление: 31.10.2015

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

Хотя идея паттернов как способ описания решения распространенных проблем в области проектирования появилась довольно давно, но их популярность стала расти во многом благодаря известной работе четырех авторов Эриха Гаммы, Ричарда Хелма, Ральфа Джонсона, Джона Влиссидеса, которая называлась "Design Patterns: Elements of Reusable Object-Oriented Software" (на русском языке известна как "Приемы объектно-ориентированного проектирования. Паттерны проектирования") и которая вышла в свет в 1994 году. А сам коллектив авторов нередко называют "Банда четырёх" или Gang of Four или сокращенно GoF. Данная книга по сути являлась первой масштабной попыткой описать распространенные способы проектирования программ. И со временем применение паттернов стало считаться хорошей практикой программирования.

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

Причем паттерны, как правило, не зависят от языка программирования. Их принципы применения будут аналогичны и в C#, и в Jave, и в других языках. Хотя в рамках данного руководства мы будем говорить о паттернах в контексте языка C#.

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

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

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

Порождающие паттерны

Порождающие паттерны - это паттерны, которые абстрагируют процесс инстанцирования или, иными словами, процесс порождения классов и объектов. Среди них выделяются следующие:

    Строитель (Builder)

    Прототип (Prototype)

    Одиночка (Singleton)

Другая группа паттернов - структурные паттерны - рассматривает, как классы и объекты образуют более крупные структуры - более сложные по характеру классы и объекты. К таким шаблонам относятся:

    Адаптер (Adapter)

    Мост (Bridge)

    Компоновщик (Composite)

    Декоратор (Decorator)

    Фасад (Facade)

    Приспособленец (Flyweight)

    Заместитель (Proxy)

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

    Команда (Command)

    Интерпретатор (Interpreter)

    Итератор (Iterator)

    Посредник (Mediator)

    Хранитель (Memento)

    Наблюдатель (Observer)

    Состояние (State)

    Стратегия (Strategy)

    Шаблонный метод (Template method)

    Посетитель (Visitor)

Существуют и другие классификации паттернов в зависимости от того, относится паттерн к классам или объектам.

Паттерны классов описывают отношения между классами посредством наследования. Отношения между классами определяются на стадии компиляции. К таким паттернам относятся:

    Фабричный метод (Factory Method)

    Интерпретатор (Interpreter)

    Шаблонный метод (Template Method)

    Адаптер (Adapter)

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

    Абстрактная фабрика (Abstract Factory)

    Строитель (Builder)

    Прототип (Prototype)

    Одиночка (Singleton)

    Мост (Bridge)

    Компоновщик (Composite)

    Декоратор (Decorator)

    Фасад (Facade)

    Приспособленец (Flyweight)

    Заместитель (Proxy)

    Цепочка обязанностей (Chain of responsibility)

    Команда (Command)

    Итератор (Iterator)

    Посредник (Mediator)

    Хранитель (Memento)

    Наблюдатель (Observer)

    Состояние (State)

    Стратегия (Strategy)

    Посетитель (Visitor)

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

И в данном руководстве мы рассмотрим наиболее основные и распространенные паттерны и принципы их использования применительно к языку C#.

Как выбрать нужный паттерн?

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

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

И в конечном счете надо придерживаться принципа KISS (Keep It Simple, Stupid) - сохранять код программы по возможности простым и ясным. Ведь смысл паттернов не в усложнении кода программы, а наоборот в его упрощении.

Перед началом статьи, я хочу предложить доклад своего товарища — Дениса Порпленко о паттернах проектирования в программировании:

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

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

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

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

Основные шаблоны программирования

Фундаментальные

Шаблон делегирования (Delegation pattern) — Объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту.
Шаблон функционального дизайна (Functional design) — Гарантирует, что каждый модуль компьютерной программы имеет только одну обязанность и исполняет её с минимумом побочных эффектов на другие части программы.
Неизменяемый интерфейс (Immutable interface) — Создание неизменяемого объекта.
Интерфейс (Interface) — Общий метод для структурирования компьютерных программ для того, чтобы их было проще понять.
Интерфейс-маркер (Marker interface) — В качестве атрибута (как пометки объектной сущности) применяется наличие или отсутствие реализации интерфейса-маркера. В современных языках программирования вместо этого могут применяться атрибуты или аннотации.
Контейнер свойств (Property container) — Позволяет добавлять дополнительные свойства для класса в контейнер (внутри класса), вместо расширения класса новыми свойствами.
Событийный шаблон (Event channel) — Расширяет шаблон Publish/Subscribe, создавая централизованный канал для событий. Использует объект-представитель для подписки и объект-представитель для публикации события в канале. Представитель существует отдельно от реального издателя или подписчика. Подписчик может получать опубликованные события от более чем одного объекта, даже если он зарегистрирован только на одном канале.

Порождающие шаблоны

Порождающие шаблоны (Creational) — шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Шаблон, порождающий классы, использует наследование, чтобы изменять инстанцируемый класс, а шаблон, порождающий объекты, делегирует инстанцирование другому объекту.
Абстрактная фабрика (Abstract factory) — Класс, который представляет собой интерфейс для создания компонентов системы.
Строитель (Builder) — Класс, который представляет собой интерфейс для создания сложного объекта.
Фабричный метод (Factory method) — Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
Отложенная инициализация (Lazy initialization) — Объект, инициализируемый во время первого обращения к нему.
Пул одиночек (Multiton) — Гарантирует, что класс имеет поименованные экземпляры объекта и обеспечивает глобальную точку доступа к ним.
Объектный пул (Object pool) — Класс, который представляет собой интерфейс для работы с набором инициализированных и готовых к использованию объектов.
Прототип (Prototype) — Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор.
Получение ресурса есть инициализация (Resource acquisition is initialization (RAII)) — Получение некоторого ресурса совмещается с инициализацией, а освобождение - с уничтожением объекта.
Одиночка (Singleton) — Класс, который может иметь только один экземпляр.

Структурные шаблоны

Структурные шаблоны (Structural) определяют различные сложные структуры, которые изменяют интерфейс уже существующих объектов или его реализацию, позволяя облегчить разработку и оптимизировать программу.
Адаптер (Adapter / Wrapper) — Объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс.
Мост (Bridge) — Структура, позволяющая изменять интерфейс обращения и интерфейс реализации класса независимо.
Компоновщик (Composite) — Объект, который объединяет в себе объекты, подобные ему самому.
Декоратор или Обёртка (Decorator) или (Wrapper) — Класс, расширяющий функциональность другого класса без использования наследования.
Фасад (Facade) — Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.
Единая точка входа (Front controller) — Обеспечивает унифицированный интерфейс для интерфейсов в подсистеме. Front Controller определяет высокоуровневый интерфейс, упрощающий использование подсистемы.
Приспособленец (Flyweight) — Это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым.
Заместитель (Proxy) — Объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него.

Поведенческие шаблоны

Поведенческие шаблоны (Behavioral) определяют взаимодействие между объектами, увеличивая таким образом его гибкость.
Цепочка обязанностей (Chain of responsibility) — Предназначен для организации в системе уровней ответственности.
Команда (Command) — Представляет действие. Объект команды заключает в себе само действие и его параметры.
Интерпретатор (Interpreter) — Решает часто встречающуюся, но подверженную изменениям, задачу.
Итератор (Iterator) — Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из объектов, входящих в состав агрегации.
Посредник (Mediator) — Обеспечивает взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на друга.
Хранитель (Memento) — Позволяет не нарушая инкапсуляцию зафиксировать и сохранить внутренние состояния объекта так, чтобы позднее восстановить его в этих состояниях.
Нулевой объект (Null object) — Предотвращает нулевые указатели, предоставляя объект «по умолчанию».
Наблюдатель (Observer) — Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
Слуга (Servant) — Используется для обеспечения общей функциональности группе классов.
Спецификация (Specification) — Служит для связывания бизнес-логики.
Состояние (State) — Используется в тех случаях, когда во время выполнения программы объект должен менять своё поведение в зависимости от своего состояния.
Стратегия (Strategy) — Предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
Шаблонный метод (Template method) — Определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
Посетитель (Visitor) — Описывает операцию, которая выполняется над объектами других классов. При изменении класса Visitor нет необходимости изменять обслуживаемые классы.
Простая политика — я знаю, что такой паттерн есть, но что он означает, пока не нашел. Если будет инфа — скиньте в комментариях.
Слушатель (Event listener) — аналогично
Одноразовый посетитель (Single-serving visitor) — Оптимизирует реализацию шаблона посетитель, который инициализируется, единожды используется, и затем удаляется.
Иерархический посетитель (Hierarchical visitor) — Предоставляет способ обхода всех вершин иерархической структуры данных (например, древовидной).

Шаблоны параллельного программирования

Используются для более эффективного написания многопоточных программ, и предоставляет готовые решения проблем синхронизации.
Активный объект (Active Object) — Служит для отделения потока выполнения метода от потока, в котором он был вызван. Использует шаблоны асинхронный вызов методов и планировщик.
Уклонитель (Balking) — Служит для выполнения действия над объектом только тогда, когда тот находится в корректном состоянии.
Привязка свойств (Binding properties) — Комбинирует несколько наблюдателей для обеспечения синхронизации свойств в различных объектах
Обмен сообщениями (Messaging design pattern (MDP)) — Позволяет компонентам и приложениям обмениваться информацией (сообщениями).
Блокировка с двойной проверкой (Double-checked locking) — Предназначен для уменьшения накладных расходов, связанных с получением блокировки.
Ассинхронные события (Event-based asynchronous) — Адресные проблемы с Асинхронным паттерном, которые возникают в программах с несколькими потоками.
Охраняемая приостановка (Guarded suspension) — Используется для блокировки выполнения действия над объектом только тогда, когда тот находится в корректном состоянии.
Полусинхронизация (Half-Sync/Half-Async) — пока нет данных про этот паттерн.
Лидеры (Leaders/followers) — пока нет данных про этот паттерн.
Замок (Lock) — Один поток блокирует ресурс для предотвращения доступа или изменения его другими потоками.
Монитор (Monitor object) — Объект, предназначенный для безопасного использования более чем одним потоком.
Реактор (Reactor) — Предназначен для синхронной передачи запросов сервису от одного или нескольких источников.
Блокировка чтение-запись (Read write lock) — Позволяет нескольким потокам одновременно считывать информацию из общего хранилища, но позволяя только одному потоку в текущий момент времени её изменять.
Планировщик (Scheduler) — Обеспечивает механизм реализации политики планирования, но при этом не зависящих ни от одной конкретной политики.
Пул потоков (Thread pool) — Предоставляет пул потоков для обработки заданий, представленных обычно в виде очереди.
Спецпотоковое хранилище (Thread-specific storage) — Служит для предоставления различных глобальных переменных для разных потоков.
Однопоточное выполнение (Single thread execution) — Препятствует конкурентному вызову метода, тем самым запрещая параллельное выполнение этого метода.
Кооперативный паттерн (Cooperative pattern) — Обеспечивает механизм безопасной остановки потоков исполнения, используя общий флаг для сигнализирования прекращения работы потоков.

Шаблоны архитектуры системы

Model-View-Controller (MVC) — Модель-представление-контроллер.
Model-View-Presenter
Model-View-View Model
Presentation-Abstraction-Control
Naked objects
Hierarchical Model–View–Controller

Enterprise шаблоны

Active Record - способ доступа к данным реляционных баз данных в объектно-ориентированном программировании.
Business Delegate
Composite Entity/Составная Сущность
Composite View
DAO (Data Access Object) Объект Доступа к Данным
Dispatcher View
Front Controller
Intercepting Filter
Registry
Service Activator
Service Locator/Локатор Службы
Service to Worker
Session Facade/Фасад Сессии
Transfer Object Assembler
Transfer Object/Объект Перемещения
Value List Handler/Обработчик Списка Значений
View Helper
Unit of Work

Другие типы шаблонов

Также на сегодняшний день существует ряд других шаблонов.
Хранилище (Repository)
Carrier Rider Mapper описывают предоставление доступа к хранимой информации.
Аналитические шаблоны описывают основной подход для составления требований для программного обеспечения (requirement analysis) до начала самого процесса программной разработки
Коммуникационные шаблоны описывают процесс общения между отдельными участниками/сотрудниками организации
Организационные шаблоны описывают организационную иерархию предприятия/фирмы
Антипаттерны (Anti-Design-Patterns) описывают, как не следует поступать при разработке программ, показывая характерные ошибки в дизайне и в реализации

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

Паттерны ООАП различаются степенью детализации и уровнем абстракции. Предлагается следующая общая классификация паттернов по категориям их применения:

Архитектурные паттерны(Architectural patterns) - множество предварительно определенных подсистем со спецификацией их ответственности, правил и базовых принципов установления отношений между ними.

Архитектурные паттерны предназначены для спецификации фундаментальных схем структуризации программных систем. Наиболее известными паттернами этой категории являются паттерны GRASP ( General Responsibility Assignment Software Pattern ). Эти паттерны относятся к уровню системы и подсистем, но не к уровню классов. Как правило, формулируются в обобщенной форме, используют обычную терминологию и не зависят от области приложения. Паттерны этой категории систематизировал и описал К. Ларман.

Паттерны проектирования (Design patterns) - специальные схемы для уточнения структуры подсистем или компонентов программной системы и отношений между ними.

Паттерны проектирования описывают общую структуру взаимодействия элементов программной системы, которые реализуют исходную проблему проектирования в конкретном контексте. Наиболее известными паттернами этой категории являются паттерны GoF ( Gang of Four), названные в честь Э. Гаммы, Р. Хелма, Р. Джонсона и Дж. Влиссидеса, которые систематизировали их и представили общее описание. Паттерны GoF включают в себя 23 паттерна. Эти паттерны не зависят от языка реализации, но их реализация зависит от области приложения.

Паттерны анализа (Analysis patterns) - специальные схемы для представления общей организации процесса моделирования.

Паттерны анализа относятся к одной или нескольким предметным областям и описываются в терминах предметной области . Наиболее известными паттернами этой группы являются паттерны бизнес-моделирования ARIS ( Architecture of Integrated Information Systems), которые характеризуют абстрактный уровень представления бизнес-процессов. В дальнейшем паттерны анализа конкретизируются в типовых моделях с целью выполнения аналитических оценок или имитационного моделирования бизнес-процессов.

Паттерны тестирования (Test patterns) - специальные схемы для представления общей организации процесса тестирования программных систем., паттерны устойчивости кода, паттерны разработки графического интерфейса пользователя и др. Паттерны этой категории описаны в работах М. Гранда, К. Бека, Дж. Тидвелла и др. Некоторые из них реализованы в популярных интегрированных средах программирования в форме шаблонов создаваемых проектов. В этом случае выбор шаблона программного приложения позволяет получить некоторую заготовку программного кода.

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

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

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

Существует три типа шаблонов:

  • структурные;
  • порождающие;
  • поведенческие.

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

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

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

Зачем нужны шаблоны проектирования?

Шаблон проектирования, по своей сути, это продуманное решение той или иной задачи. Если вы столкнулись с известной задачей, почему бы не использовать готовое решение, проверенное опытом?

Пример

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

Class StrategyAndAdapterExampleClass { private $_class_one; private $_class_two; private $_context; public function __construct($context) { $this->_context = $context; } public function operation1() { if($this->_context == "context_for_class_one") { $this->_class_one->operation1_in_class_one_context(); } else ($this->_context == "context_for_class_two") { $this->_class_two->operation1_in_class_two_context(); } } }

Просто, не правда ли? Давайте посмотрим поближе на шаблон «Стратегия».

Шаблон «Стратегия»

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

В примере выше выбор стратегии основан на значении переменной $context , которое было в момент создания объекта. Если значение было "context_for_class_one" , программа будет использовать класс class_one . И наоборот.

Хорошо, но где это можно использовать?

Представьте, что вы разрабатываете класс, который может создать или обновить запись в базе данных. В обоих случаях входные параметры будут одни и те же (имя, адрес, номер телефона и т. п.), но, в зависимости от ситуации, он будет должен использовать различные функции для обновления и создания записи. Можно каждый раз переписывать условие if/else , а можно создать один метод, который будет принимать контекст:

Class User { public function CreateOrUpdate($name, $address, $mobile, $userid = null) { if(is_null($userid)) { // пользователя не существует, создаем запись } else { // запись есть, обновляем ее } } }

Обычно шаблон «Стратегия» подразумевает инкапсуляцию алгоритмов в классы, но в данном случае это излишне. Помните, что вы не обязаны следовать шаблону слово в слово. Любые варианты допустимы, если они решают задачу и соответствуют концепции.

Шаблон «Адаптер»

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

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

Как его использовать?

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

Сравните два примера.

Без адаптера
$user = new User(); $user->CreateOrUpdate(// параметры); $profile = new Profile(); $profile->CreateOrUpdate(// параметры);

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

С использованием адаптера

Мы можем создать класс-обертку Account:

Class Account() { public function NewAccount(// параметры) { $user = new User(); $user->CreateOrUpdate(// часть параметров); $profile = new Profile(); $profile->CreateOrUpdate(// часть параметров); } } $account_domain = new Account(); $account_domain->NewAccount(// параметры);

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

Шаблон «Метод-фабрика»

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

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

Как его использовать?

Фабрика обычно используется для создания различных вариантов базового класса. Допустим, у вас есть класс кнопки - Button - и три варианта - ImageButton , InputButton и FlashButton . С помощью фабрики вы можете создавать различные варианты кнопок в зависимости от ситуации.

Сначала создадим три класса:

Abstract class Button { protected $_html; public function getHtml() { return $this->_html; } } class ImageButton extends Button { protected $_html = "..."; // HTML-код кнопки-картинки } class InputButton extends Button { protected $_html = "..."; // HTML-код обычной кнопки (); } class FlashButton extends Button { protected $_html = "..."; // HTML-код Flash-кнопки }

Теперь мы можем написать нашу фабрику:

Class ButtonFactory { public static function createButton($type) { $baseClass = "Button"; $targetClass = ucfirst($type).$baseClass; if (class_exists($targetClass) && is_subclass_of($targetClass, $baseClass)) { return new $targetClass; } else { throw new Exception("The button type "$type" is not recognized."); } } }

и использовать ее:

$buttons = array("image","input","flash"); foreach($buttons as $b) { echo ButtonFactory::createButton($b)->getHtml() }

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

Шаблон «Декоратор»

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

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

Для реализации декоратора нам понадобится:

  1. Унаследовать класс-декоратор от базового.
  2. Добавить поле со ссылкой на базовый класс в декоратор.
  3. Передать ссылку на декорируемый объект в конструктор декоратора.
  4. Перенаправить методы из декоратора на декорируемый объект.
  5. Переопределить методы в декораторе, поведение которых необходимо изменить.

Как его использовать?

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

Сначала определимся, какие «декорации» нам нужны:

  • Если мы на заглавной странице и вошли в аккаунт, ссылка должна быть в h2 -теге.
  • Если мы на любой другой странице и вошли в аккаунт, ссылка должна быть подчеркнутой.
  • Если мы вошли в аккаунт, ссылка должна быть в strong -теге.

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

Class HtmlLinks { // методы для работы с любой HTML-ссылкой } class LogoutLink extends HtmlLinks { protected $_html; public function __construct() { $this->_html = "Logout"; } public function setHtml($html) { $this->_html = $html; } public function render() { echo $this->_html; } } class LogoutLinkH2Decorator extends HtmlLinks { protected $_logout_link; public function __construct($logout_link) { $this->_logout_link = $logout_link; $this->setHtml("" . $this->_html . ""); } public function __call($name, $args) { $this->_logout_link->$name($args); } } class LogoutLinkUnderlineDecorator extends HtmlLinks { protected $_logout_link; public function __construct($logout_link) { $this->_logout_link = $logout_link; $this->setHtml("" . $this->_html . ""); } public function __call($name, $args) { $this->_logout_link->$name($args); } } class LogoutLinkStrongDecorator extends HtmlLinks { protected $_logout_link; public function __construct($logout_link) { $this->_logout_link = $logout_link; $this->setHtml("" . $this->_html . ""); } public function __call($name, $args) { $this->_logout_link->$name($args); } }

Теперь мы можем использовать их так:

$logout_link = new LogoutLink(); if($is_logged_in) { $logout_link = new LogoutLinkStrongDecorator($logout_link); } if($in_home_page) { $logout_link = new LogoutLinkH2Decorator($logout_link); } else { $logout_link = new LogoutLinkUnderlineDecorator($logout_link); } $logout_link->render();

Обратите внимание, как можно использовать несколько декораторов на одном объекте. Все они используют функцию __call для вызова оригинального метода. Если мы войдем в аккаунт и перейдем на заглавную страницу, результат будет такой:

Logout

Шаблон «Одиночка»

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

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

Как его использовать?

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

Class Session { private static $instance; public static function getInstance() { if(is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } private function __construct() { } private function __clone() { } // прочие методы сессии... ... ... } // get a session instance $session = Session::getInstance();

Теперь мы можем получить доступ к сессии из различных участков кода, даже из других классов. Метод getInstance всегда будет возвращать одну и ту же сессию.

Заключение

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

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



В продолжение темы:
Windows

Часть вторая : "Важнейшие характеристики каждого семейства процессоров Intel Core i3/i5/i7. Какие из этих чипов представляют особый интерес" Введение Сначала мы приведём...

Новые статьи
/
Популярные