Мартин Фаулер

Написание паттернов проектирования

(перевод — Афонов И.В.)

Martin Fowler
Оригинал

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

Содержание

Что такое паттерн
Паттерны и рецепты
Важные части паттернов
Паттерны — решения
Выразительное наименование
Мотив
Пример кода
Общие формы паттернов
Александрийская форма
GOF
Portland-form
Coplien
POSA
P of EAA
Выбор формы паттерна

Что такое паттерн

Общее определение паттерна — это решение проблемы в текущем контексте. Это определение всегда казалось мне абсолютно бесполезным.

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

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

Таким образом, для удачного решения проблемы каждый паттерн в первую очередь должен обозначить решение. Данное решение должно быть конкретным, по крайней мере на уровне обсуждаемой проблемы. Человек должен иметь возможность пользоваться решением, просто получив ссылку на название паттерна. Если название для решения выбрано удачно — название войдёт в словарь профессионалов. Это может занять время, но когда вы говорите «decorator» любой профессиональный программист будет знать, о чём вы говорите.

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

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

Сравнение паттернов и рецептов

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

Как следствие этого рецепты больше сфокусированы на проблеме чем на её решении.

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

Почему паттерны важны?

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

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

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

Важные части паттернов

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

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

Паттерны — решения.

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

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

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

Запоминающееся имя

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

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

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

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

«Почему» так же важно как «Каким образом»

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

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

Я особенно с подозрением отношусь к целым языкам паттернов, которые описывают только один набор альтернатив. Одним из инициаторов создания P EAA было моё раздражение вызванное людьми, которые говорили о Единой Архитектуре для использования в J2EE. Системы программного обеспечения, даже в пределах специфической области, например в секторе, enterprise, всё-таки живут в разнообразном мире. Есть много способов создать некоторые вещи, и часто большинство из них применимо только при некоторых данных обстоятельствах. Так, всякий раз, когда Вы думаете, «Вы никогда не должны делать, что-то», подумайте об это ещё раз. Вполне возможно, что вы не только придёте к другому паттерну, но это также поможет вам более полно понять вашу текущую задачу.

Общие формы паттернов

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

Форма Александра.

Много люди считают что Язык Паттернов (язык АПЛ) Кристофера Александра оказал очень важное влияние на сферу паттернов проектирования. Он написал его книгу образцов в специфической форме, которая известна в мире паттернов программного обеспечения как александрийская форма. Так же как паттерны в его книге, Вы можете также найти хорошие примеры этой формы в Domain Driven Design. Для web хорошим примером является — Pools of Insight Джоша Керивски.

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

Для удобства и ясности, все паттерны имеют единый формат. Сначала должна быть приведена схема, которая показывает пример, как может быть реализован паттерн средствами ООП. После этой схемы каждый паттерн должен иметь небольшой вводный параграф, в котором должен быть представлен контекст, в котором можно применить данный паттерн. После этого должны быть три отметки, которые маркируют собой начало описания проблемы, после этого идёт заголовок проблемы, выделенный жирным шрифтом, заголовок кратко, в двух-трёх предложениях описывает проблему, после этого идёт полное описание проблемы, это самая длинная секция в ней подробно должна быть описана проблема и возможные варианты её решения с помощью данного паттерна. После описания проблемы снова жирным шрифтом должно быть выделено решение — основная часть паттерна, которая описывает поле физических и социальных отношений, которые необходимы для решения проблемы в данном контексте. Решение всегда должно быть представлено в форме инструкции — таким образом, чтобы вы всегда знали, что точно надо сделать, чтобы реализовать данный паттерн. После решения должна быть представлена диаграмма, где должны быть отмечены основные метки решения.
После диаграммы решения, должны снова стоять три метки, которые показывают, что основная часть паттерна окончена. В данной секции могут быть представлены примеры реализации паттерна на различных языках.
Паттерны на описываемые на языке APL обычно занимают в среднем 6–7 страниц текста.
Александрийская форма является более повествовательной формой с небольшим количеством заголовков, в результате паттерны, описанные таким образом лучше воспринимаются при чтении. Выделенные жирным шрифтом итоговые предложения позволяют быстро просмотреть паттерн, пропустив его тело.
В паттернах программирования, которые используют александрийскую форму, часто как один из вариантов представления разделяют основную секцию на две. В первой части приводится, после заголовка проблемы, расширенное описание проблемы, а во второй после описания решения приводится более полное его описание и возможные проблемы которые могут возникнуть при его реализации.
Я услышал, что Ричард Габриэль критиковал это, на том основании, что это вынуждает вас дублировать большую часть обсуждения альтернатив. Я не думал слишком много об этом прежде, но я нахожу, что я соглашаюсь с ним сейчас. Сокращенное тело описания ломает поток описания паттерна и делает это более изменчивым, поскольку вы волнуетесь о том, должны ли проблемы быть обсуждены в проблемной части или в части описания решения.

GOF Форма

Форма GOF была использована в книге Gang of Four, после которой паттерны проектирования действительно начали использоваться повсеместно. Это — очень структурированная форма, разбивающая паттерн на много частей: Намерение, Побуждение, Применимость, Структура, Участники, Сотрудничество, Последствия, Выполнение, Типовой Код, Известные Использования, и Связанные Паттерны. Описание паттерна с помощью данной формы является довольно большим по объёму и в среднем занимает 12 страниц.

Форма Portland. 

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

Форма Coplien

Форма представления названа так, т.к обычно она ассоциируется с Джимом Коплином, так же я слышал как многие люди называют её канонической формой. Хороший пример онлайн — coplien-fault-patterns.
В последнее время данная форма претерпела много изменений. Ключевыми элементами описания являются Проблема, Контекст, Мотивация, и Решение. Большинство авторов добавляет несколько дополнительных секций. Каждая секция — несколько параграфов, с секцией мотивации обычно представленной списком. Паттерны в этой форме обычно довольно коротки — несколько страниц. 

Форма POSA

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

P of EAA

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

Выбор вашей формы представления паттерна 

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

Хорошим началом будет чтение. Прочитайте как можно больше различных книг, в которых представлены сборники паттернов. Сконцентрируйтесь на содержимом, но при этом обратите внимание на форму, в которой представлен данный паттерн. Чтобы действительно оценить это постарайтесь не только прочитать паттерн от начала до конца, но и попробовать прочитать его пропуская некоторые секции. Вполне возможно, что вы и так постоянно занимаетесь этим в ходе своей работы, обратите внимание какие паттерны более всего вам подходят
Как только вам покажется, что вы выбрали ту форму для представления, которая вам наиболее подходит — начинайте писать. Попробуйте поэкспериментировать с различными формами паттерна. Очень хорошим упражнением является написание одного паттерна в различных формах, для того чтобы посмотреть, какая из них наиболее подходит для вас. Попробуйте дать ваш материал нескольким вашим знакомым для просмотра и оценки. Не бойтесь экспериментировать, я потратил несколько лет на то чтобы найти ту форму представления, которая наиболее подходит для меня.
Как только вы выбрали ту форму которая вам подходит, не давайте ей зажимать вас в рамки и влиять на содержание паттерна. Я часто замечаю такую проблему, особенно когда используются жёстко структурированные формы, такие как GOF или POSA — люди чувствуют что они должны добавить что-то в каждую секцию, даже если в этом нет прямой необходимости. В такой ситуации лучше пропустить секцию, чем добавлять в неё бесполезную информацию.

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

Описания паттернов могут иметь различный объём. Портлендская форма может иметь объём состоящий из несколько абзацев, в то время как форма POSA может занимать 10–15 страниц. Ваш выбор должен зависеть от того, какое количество деталей реализации вы хотите представить. Если вы хотите представить примеры кода, то тогда вы будете получать значительно более длинные описания, в этом случае структурированные формы более применимы.