Введение

Многоразовый подход к проектированию кода, реализующего чётко определённую функцию.

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

История

Шаблоны возникли как архитектурная концепция Кристофера Александра еще в 1977 году (см., например, "Модель улиц", JOURNAL OF THE AIP, сентябрь 1966 года, том 32, № 5, с. 273–278). В 1987 году Кент Бек и Уорд Каннингэм начали экспериментировать с идеей применения шаблонов к программированию – в частности, языкам шаблонов – и представили свои результаты на конференции OOPSLA в том же году. В последующие годы Бек, Каннингэм и другие продолжили эту работу. Шаблоны проектирования получили популярность в информатике после публикации в 1994 году книги "Шаблоны проектирования: элементы повторно используемого объектно-ориентированного программного обеспечения" (Design Patterns: Elements of Reusable Object Oriented Software) так называемой "Четверки авторов" (Gamma et al.), часто сокращаемой как "GoF". В том же году состоялась первая конференция по языкам шаблонов программирования, а в следующем году был создан Портлендский репозиторий шаблонов для документирования шаблонов проектирования. Область применения этого термина остается предметом дискуссий. Заметные книги в жанре шаблонов проектирования включают:

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

Практика

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

Мотив

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

Специфические для конкретной области модели

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

Объектно-ориентированное программирование

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

Примеры

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

Творческие модели

Название Описание В шаблонах проектирования В коде Завершенное Другие Абстрактная фабрика Предоставляет интерфейс для создания семейств связанных или зависимых объектов, не указывая их конкретные классы. Строитель Отделяет построение сложного объекта от его представления, позволяя одному и тому же процессу построения создавать различные представления. Внедрение зависимостей Класс принимает необходимые ему объекты от внедряющего механизма, а не создает их напрямую. Фабричный метод Определяет интерфейс для создания единственного объекта, но позволяет подклассам решать, какой класс инстанцировать. Фабричный метод позволяет классу отложить инстанцирование подклассам. Ленивая инициализация Тактика отсрочки создания объекта, вычисления значения или другого ресурсоемкого процесса до момента первого обращения к нему. Эта модель встречается в каталоге GoF как "виртуальный прокси", стратегия реализации для шаблона "Прокси". Мультон Обеспечивает наличие у класса только именованных экземпляров и предоставляет глобальную точку доступа к ним. Пул объектов Позволяет избежать дорогостоящего выделения и освобождения ресурсов за счет повторного использования объектов, которые больше не нужны. Может рассматриваться как обобщение шаблонов "пул соединений" и "пул потоков". Прототип Определяет типы объектов, которые нужно создавать, используя прототипный экземпляр, и создает новые объекты на основе "скелета" существующего объекта, тем самым повышая производительность и минимизируя использование памяти. Приобретение ресурса – это инициализация (RAII) Обеспечивает правильное освобождение ресурсов путем привязки их к жизненному циклу соответствующих объектов. Одиночка Обеспечивает наличие у класса только одного экземпляра и предоставляет глобальную точку доступа к нему.

Поведенческие модели

Название Описание в шаблонах проектирования в коде Завершение других активных объектов Отделяет выполнение метода от вызова метода, находящегося в собственной нити управления. Цель — обеспечить параллельность, используя асинхронный вызов метода и планировщик для обработки запросов. Задержка (Balking) Выполняет действие над объектом только тогда, когда объект находится в определенном состоянии. Связывание свойств Объединяет несколько наблюдателей, чтобы заставить свойства в разных объектах синхронизироваться или координироваться каким-либо образом. Вычислительное ядро Многократно выполняет одно и то же вычисление параллельно, различаясь целочисленными параметрами, используемыми с безынструкционной векторной математикой для доступа к общим массивам, например, оптимизированное для GPU умножение матриц или свёрточная нейронная сеть. Двойная проверка блокировки (Double-checked locking) Снижает накладные расходы на получение блокировки, сначала проверяя критерий блокировки («подсказку блокировки») небезопасным способом; только в случае успеха выполняется фактическая логика блокировки. Может быть небезопасным при реализации в некоторых комбинациях языка/аппаратного обеспечения и поэтому иногда рассматривается как антипаттерн. Событийно-ориентированная асинхронность Решает проблемы, возникающие с асинхронным паттерном в многопоточных программах. Защищенная приостановка (Guarded suspension) Управляет операциями, требующими получения блокировки и выполнения предварительного условия перед выполнением операции. Join Паттерн Join предоставляет способ написания параллельных, конкурентных и распределенных программ посредством передачи сообщений. По сравнению с использованием потоков и блокировок, это модель программирования высокого уровня. Блокировка (Lock) Один поток устанавливает «блокировку» на ресурс, предотвращая доступ или изменение его другими потоками. PoEAA Также известен как «поток на сессию». Пул потоков (Thread pool) Создается несколько потоков для выполнения ряда задач, обычно организованных в очередь. Обычно задач намного больше, чем потоков. Может рассматриваться как частный случай паттерна «пул объектов». Хранилище, специфичное для потока (Thread-specific storage) Статическая или «глобальная» память, локальная для потока. Безопасная параллельность с исключительной собственностью (Safe Concurrency with Exclusive Ownership) Избегает необходимости в механизмах параллельной работы во время выполнения, поскольку исключительную собственность можно доказать. Это важная возможность языка Rust, но проверка во время компиляции — не единственный способ; программист часто вручную проектирует такие паттерны в код, опуская использование механизма блокировки, поскольку оценивает, что к данной переменной никогда не будет одновременный доступ. Атомная операция процессора (CPU atomic operation) Архитектуры x86 и других процессоров поддерживают ряд атомарных инструкций, гарантирующих безопасность памяти при изменении и доступе к примитивным значениям (целым числам). Например, два потока могут безопасно увеличивать счетчик. Эти возможности также могут использоваться для реализации механизмов для других паттернов параллельности, описанных выше. Язык C# использует класс Interlocked для этих целей.

Документация

В документации к шаблону проектирования описывается контекст его использования, силы, действующие в этом контексте, которые шаблон призван разрешить, и предлагаемое решение. Не существует единого стандартного формата для документирования шаблонов проектирования. Скорее, различные авторы шаблонов использовали разные форматы. Однако, по мнению Мартина Фаулера, некоторые формы шаблонов стали более распространёнными, чем другие, и, следовательно, часто служат отправной точкой при создании новых шаблонов. Одним из примеров широко используемого формата документации является формат, использованный Эрихом Гаммой, Ричардом Хелмом, Ральфом Джонсоном и Джоном Виссидесом в их книге «Design Patterns». Он включает следующие разделы:

Название и классификация шаблона: описательное и уникальное название, помогающее идентифицировать и ссылаться на шаблон. Назначение: описание цели шаблона и причин его использования. Также известен как: другие названия шаблона. Обоснование (Силы): сценарий, включающий проблему и контекст, в котором этот шаблон может быть применен. Область применимости: ситуации, в которых этот шаблон применим; контекст для шаблона. Структура: графическое представление шаблона. Для этого могут использоваться диаграммы классов и диаграммы взаимодействия. Участники: перечисление классов и объектов, используемых в шаблоне, и их роли в проектировании. Взаимодействие: описание того, как классы и объекты, используемые в шаблоне, взаимодействуют друг с другом. Последствия: описание результатов, побочных эффектов и компромиссов, возникающих при использовании шаблона. Реализация: описание реализации шаблона; часть шаблона, представляющая решение. Пример кода: иллюстрация того, как шаблон может быть использован в языке программирования. Известные примеры использования: примеры реального применения шаблона. Связанные шаблоны: другие шаблоны, имеющие отношение к данному шаблону; обсуждение различий между шаблоном и похожими шаблонами.

Критика

Некоторые полагают, что шаблоны проектирования могут указывать на отсутствие необходимых функций в конкретном языке программирования (например, Java или C++). Питер Норвиг показывает, что 16 из 23 шаблонов, описанных в книге "Design Patterns" (ориентированной преимущественно на C++), упрощаются или исключаются (благодаря прямой поддержке языка) в Lisp или Dylan. Схожие наблюдения сделали Ханнеман и Кицалес, реализовав несколько из 23 шаблонов проектирования с использованием аспектно-ориентированного языка программирования (AspectJ) и продемонстрировав, что зависимости на уровне кода были устранены из реализации 17 из 23 шаблонов, а аспектно-ориентированное программирование способно упростить их реализацию. См. также эссе Пола Грэма "Месть гиков". Некорректное применение шаблонов может неоправданно усложнить систему. По определению, каждый шаблон необходимо перепрограммировать в каждом приложении, которое его использует. Поскольку некоторые авторы рассматривают это как отступление от повторного использования программного обеспечения, обеспечиваемого компонентами, исследователи работали над преобразованием шаблонов в компоненты. Майеру и Арноу удалось компонетизировать полностью или частично две трети шаблонов, которые они пытались реализовать. Для обеспечения гибкости шаблоны проектирования могут вводить дополнительные уровни косвенности, что может усложнить конечную архитектуру и снизить производительность во время выполнения.