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

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

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

Классификация паттернов

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

Паттерны делятся на следующие категории:

  • Архитектурные паттерны
  • Паттерны проектирования
  • Идиомы

Архитектурные паттерны, являясь наиболее высокоуровневыми паттернами, описывают структурную схему программной системы в целом. В данной схеме указываются отдельные функциональные составляющие системы, называемые подсистемами, а также взаимоотношения между ними. Примером архитектурного паттерна является хорошо известная программная парадигма "модель-представление-контроллер" (model-view-controller - MVC). В свою очередь, подсистемы могут состоять из архитектурных единиц уровнем ниже.

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

Идиомы, являясь низкоуровневыми паттернами, имеют дело с вопросами реализации какой-либо проблемы с учетом особенностей данного языка программирования. При этом часто одни и те же идиомы для разных языков программирования выглядят по-разному или не имеют смысла вовсе. Например, в C++ для устранения возможных утечек памяти могут использоваться интеллектуальные указатели. Интеллектуальный указатель содержит указатель на участок динамически выделенной памяти, который будет автоматически освобожден при выходе из зоны видимости. В среде Java такой проблемы просто не существует, так как там используется автоматическая сборка мусора. Обычно, для использования идиом нужно глубоко знать особенности применяемого языка программирования. Следует отметить, что в программной области существуют и другие виды паттернов, не относящиеся к проектированию вообще, например, паттерны анализа, тестирования, документирования и др.

Типы паттернов проектирования

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

  • Абстрактная фабрика (Abstract factory) - класс, который представляет собой интерфейс для создания компонентов системы.
  • Фабричный метод (Factory method) - определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
  • Прототип (Prototype) - определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор.
  • Строитель (Builder) - класс, который представляет собой интерфейс для создания сложного объекта.
  • Одиночка (Singleton) - класс, который может иметь только один экземпляр.
  • Отложенная инициализация (Lazy initialization) - объект, инициализируемый во время первого обращения к нему.

Структурные паттерны

  • Адаптер (Adapter) - объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс.
  • Компоновщик (Composite) - объект, который объединяет в себе объекты, подобные ему самому.
  • Декоратор или Обёртка (Decorator) - класс, расширяющий функциональность другого класса без использования наследования.
  • Фасад (Facade) - объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.
  • Единая точка входа (Front Controller) - обеспечивает унифицированный интерфейс для интерфейсов в подсистеме. Front Controller определяет высокоуровневый интерфейс, упрощающий использование подсистемы.
  • Заместитель (Proxy) - объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него.

Поведенческие паттерны

  • Команда, Экшен, Транзакция (Command) - представляет действие. Объект команды заключает в себе само действие и его параметры.
  • Стратегия (Strategy) - предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
  • Шаблонный метод (Template method) - определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
  • Наблюдатель, Слушатель (Observer) - определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
  • Цепочка обязанностей (Chain of responsibility) - предназначен для организации в системе уровней ответственности.

Данной статьей мы начинаем серию статей, посвященных паттернам проектирования.

Статьи рассчитаны на тех, кто уже хорошо знает ООП.

Что такое паттерны в программировании

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

Паттерн - это повторяющийся элемент в различных сферах жизни.

Пример 1: окрас тигра - это паттерн.

Пример 2: Коробка передач - это паттерн.

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

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

Шаблон проектирования / шаблон программирования / паттерн - это типичные способы решения часто возникающих задач в сфере разработки ПО.

ВАЖНО:

Паттерн - это не готовое решение, которое можно откуда-то скопировать и вставить в Вашу программу. Это только общие принципы, которые надо уметь правильно применить.

Мне надо знать паттерны?

Ответ: "Да"

Потому что:

  • Паттерны очень часто применяются на практик е. Конечно, для начинающих программистов понимание паттернов не всегда заходит легко. Так что наберитесь терпения и учим, учим, учим...
  • Паттерны часто спрашивают на собеседованиях.
  • И самое главное - паттерны предлагают Вам готовые решения . Они помогут Вам сохранить время и усилия, а качество программы повысится.

Откуда они взялись

Хотя сама идея паттернов далеко не новая, популярной она стала после выхода книги "Приёмы объектно-ориентированного проектирования. Паттерны проектирования ". Это произошло в 1994 году. С тех пор мир захватила "шаблономания" 🙂

Какие они бывают

  • Порождающие (Creational Design Patterns)

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

  • Структурные (Structural Design Patterns)

Отвечают за иерархию классов и интерфейсов. Например, "как заставить объекты с несовместимыми интерфейсами работа вместе"?

  • Поведенческие (Behavioral Design Patterns)

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

Из чего состоит паттерн?

  • Задача, которую решает паттерн
  • Решение:
    • Структуры классов, составляющих решение;
    • Примера на одном из языков программирования;
  • Связь с другими паттернами

А конкретнее?

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

Здесь о каждом из них мы, конечно, говорить не будем - это много 🙂 Но мы расскажем об основных паттернах в будущих статьях.

Самыми-самыми "базовыми" шаблонами проектирования можно назвать следующие:

  • Singleton ("Одиночка")
  • Builder ("Строитель")
  • Factory ("Фабрика")
  • Wrapper ("Обертка")
  • Proxy ("Прокси")

С них можно начинать изучение паттернов. Ниже в этой статье Вы найдете ссылочки на статьи по этим паттернам.

По своей сути - паттерны - это обычные шаблоны проектирования. Заимствовано у обычных архитекторов (те, которые зданиями занимаются). Суть проста. В работе архитектора есть задачи, которые удобно решать одним или несколькими проверенными способами.

По аналогии в проектировании софта имееются свои архитектурные вопросы вроде разбиения приложения на компоненты/модули, организации зависимостей между ними, распределение функциональных обязанностей и т.п. Как ловко подметили авторы книжки из этой банды четырех (The "Gang of Four") в нашей индустрии можно также выделить некоторе количество типовых шаблонов, проверенных на практике, чтобы тем самым не наступать на уже обойденные другими грабли.

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

Чтобы понять, где они нужны - нужен опыт. То есть (я лично убежден), что учиться на ошибках других может только крайне ограниченное число людей. Все остальные обязаны набить шишки самостоятельно:)

К изучению паттернов я дам такие советы:

1) Прочтите пару книжек, чтобы понять, что это за зверь и с чем его едят. Можно взять одну из вариаций книжки GoF или какие-то производные для вашего стека разработки - познакомиться с основными популярными шаблонами. Сразу после этого я посоветовал бы прочесть книжку "Горький вкус Java" (Брюс Тейт) - она про анти-паттерны. Это чтобы понять обратную сторону их использования. Мне понравилась и уберегла думаю от многих проблем. То что на примере Java - неважно. Речь идет о шаблонах, так что представителям других стеков (к которым отношусь и я) будет просто понять все равно.

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

3) В новых проектах, держите в голове полученные по шаблонам знания - вдруг пригодятся.

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

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

Среди прочих - такие как maintainability (простая поддержка кода), scalability (масштабируемость), extensibility (расширяемость), availability (устойчивость) и тп. Если, проектируя свое приложение, вы задумываетесь об этих "илитях" и стараетесь их обеспечить в необходимом проекту объеме, то, как правило, ваше приложение будет иметь отличную архитектуру. При этом шаблоны проектирования в ней появятся лаконично сами собой.

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

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

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

Вы хотите создать автомобиль, но поняитя не имеете, с чего начать. Сколько должно быть у него колес? 3, 4, 5? Вы не знаете точно, потому что никогда до этого не занимались проектированием автомобилей. К счастью, до вас люди занимались этим десятилетиями и вы точно знаете, что для конкретно вашего варианта автомобиля потребуется база из 4 колес. Вам не нужно экспериментировать и строить трицикл, чтобы убедиться в его неэффективности.

Паттерны не привязаны к конкретному языку программирования, это просто подход к проектированию.

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

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

Singleton (одиночка)

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

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

Но главное в одиночке то, что создав станцию один раз, ей может пользоваться сколько угодно людей. Смысл в том, что когда вы скажете «Мне нужна телефонная станция», вам ответят не «Нужно построить новую», а «Она находится там-то».

Registry (реестр, журнал записей)

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

Еще один пример одиночки-реестра – бухгалтерия. Фирма не создает бухгалтерию каждый раз, когда она ей понадобится. В то же время, в бухгалтерии хранятся записи обо всех сотрудниках фирмы, как в реестре.

Multiton (пул «одиночек»)

По сути данный паттерн – это реестр одиночек, каждый из которых имеет имя, по которому к нему можно получить доступ.

Object pool (пул объектов)

Этот паттерн также как и предыдущий, содержит набор объектов, но не все они обязаны быть одиночками.

Factory (фабрика)

Фабрика – достаточно точное название для этого паттерна. Когда вам понадобится пакет сока, вы обращаетесь к фабрике с соответствующим запросом, она в свою очередь копирует эталон и передает вам его экземпляр. Что при этом происходит внутри фабрики и как она это делает вас не беспокоит.

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

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

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

Prototype (прототип)

Этот паттерн похож на фабрику, но только фабрика здесь в самом объекте. К примеру, у вас в руках есть пустой пакет для сока, которому вы говорите «Хочу ананасовый сок». Пакет в свою очередь копирует себя и заполняет себя ананасовым соком.

В данном случае, пакет является прототипом и создает на своей основе другие объекты, с требуемыми вам параметрами.

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

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

Допустим фабрика производит пакеты с разными соками. Мы можем на каждый вид сока сделать свою производственную линию, но это не эффективно. Удобнее сделать одну линию по производству пакетов-основ, а разделение ввести только на этапе заливки сока, который мы можем определять просто по названию сока.

Для этого мы создаем основной отдел по производству пакетов-основ и предупреждаем все подотделы, что они должны производить нужный пакет с соком про простому «Надо» (т.е. каждый подотдел должен реализовать паттерн «фабричный метод»). Поэтому каждый подотдел заведует только своим типом сока и реагирует на слово «Надо».

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

Lazy initialization (отложенная инициализация)

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

Dependency injection (внедрение зависимости)

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

«Внедрение зависимости» позволяет перекладывать и взаимозаменять отдельные части программы без потери общей функциональности.


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

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

Так что посоветую 2 варианта изучения.
1) Тупо работаешь над сложные проектами, только действительно сложными, а не сайтиками на cms. И со временем ты начинаешь встречаться с проблемами. Тогда открываешь паттерны и тебе не придется даже как то их особо понимать, потому что это будет естевственно для тебя. Я думаю ты используешь ide вместо редактора кода. Но к примеру я помню тот момент, когда я пользовался саблаймом и знал, что есть ide, но я писал на тот момент простые вещи и когда мне говорили, почему я не юзаю ide, ведь в ней столько всего, я не понимал их потому что мне и саблайма за глаза хватало. Но пришло время, когда надо было то и се и саблайма стало мало. И тут открываю ide, а там уже есть все необходимое и думаешь в такие моменты, как я раньше этим не пользовался. А дело в том, что раньше и не надо было. Может неудачный пример, но вы поняли) Конечно, этот вариант изучения не совсем реален, по скольку сложный проект еще найти надо, да еще попасть в команду, которая не говнокодит, так как и крупные проекты бывают достаточно плохо написаны. Но можно как вариант к примеру делать свою cms и применять в ней как можно больше паттернов.

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

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