“Шаблоны проектирования помогают вам научиться на чужих успехах вместо своих неудач[2].”

Возможно, что большинство важных шагов на пути к объектно-ориентированному дизайну являлось движением к "шаблонам проектирования ", которое описано в Design Patterns (ibid)[3]. Эта книга показывает 23 разных решения особого класса проблем. В ней основная концепция шаблонов проектирования приводится наряду с примерами. Это должно подтолкнуть вас к прочтению Design Patterns написанную Gamma, et. al., текст которой теперь стал неотъемлемой частью, едва ли не обязательным справочником для ООП программистов.

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

Что такое шаблон?

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

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

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

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

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

Вы также видели и другой шаблон, который появился в Design Patterns: итератор (Java 1.0 и 1.1 капризно называл их Enumeration; контейнеры Java 2 используют "итераторы"). Это прячет обычную реализацию контейнера, в то время, как вы перебираете элементы и выбираете элементы по одному. Такой ваш код может быть использован с любым контейнером, который может породить итератор.

Таксономия шаблона

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

  1. Идиома: как мы пишем код на определенном языке для выполнения определенных действий. Это может быть чем-то столь же общим, как процесс кодирования продвижения по массиву на C (когда мы не доходим до конца).

  2. Специфичный дизайн: решение, к которому мы пришли при работе над определенной проблемой. Это может быть искусный дизайн, но он не делает попыток к обобщению.

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

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

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

Можно так же привести доводы в пользу включения Анализа Шаблона и Архитектуры Шаблоны в эту таксономию.

Принципы дизайна

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

  • Принцип наименьшего удивления (не удивляйте).
  • Делайте общие вещи проще, а редкие вещи возможными
  • Последовательность. Одна вещь стала для меня очень понятной, особенно из-зи Python: много случайных правил, которые вы выдаете программисту, которые не имеют ничего общего с решением проблемы, замедляют работу программиста. И при этом наблюдается не линейная, а экспоненциальная зависимость.
  • Закон Деметера (Demeter): "Не говорите со странниками". Объект должен ссылаться только на себя, свои атрибуты и аргументы своих методов.
  • Вычитание: дизайн закончен, когда вы уже не можете ничего убрать.
  • Упрощение перед обобщением[5].(Вариация Оккама Разора (Occam Razor), который говорил: "простые решения - лучше".) Общая проблема, которую мы находим в разрабатываемых структурах, состоит в том, что они предназначены для общих целей, независимо от реальной системы. Это приводит ошеломляющему набору настроек, которые часто не используются, используются неправильно или просто бесполезны. Однако, большинство разработчиков работают над определенной системой, и поиск общности не всегда способствует этому. Лучший путь к общности проходит через понимание правильно установленной специфики примера. Таким образом, этот принцип работает как разделитель между одинаково жизнеспособными альтернативами дизайна. Конечно, всегда возможно, что более простое решение будет более общим.
  • Рефлексивность (предложенный мной термин). Одна абстракция на класс, один класс на абстракцию. Также можно назвать Изоморфизм.
  • Независимость или Ортогональность. Выражайте независимые идеи независимо. Это полное Разделение, Инкапсуляция и Изменение, и это часть выражения "низкая связанность - большое единение".
  • Однажды и только однажды: Предотвращайте дублирование логики и структуры, где дублирование не случайно, то есть где обе части кода преследуют одинаковую цель по одной и той же причине.

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

Классификация шаблонов

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

  1. Созидательная: как объект может быть создан. Часто это вовлекает изолирование деталей создания объекта, так что ваш код не зависит от типа объекта, и поэтому его не нужно менять, когда вы добавляете новый тип объекта. Вышеупомянутый Singleton классифицируется, как созидательный шаблон, и далее в этой книге вы увидите примеры производящих методов (Factory Metod) и Прототипов (Prototype).

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

  3. Поведенческая: объекты управляют определенными типами действий в программе. Это инкапсулирует процесс, который вы хотите выполнить, такой как интерпретирование языка, выполнение запроса, перемещение по последовательности (как в итераторе) или реализация алгоритма. Эта книга содержит примеры шаблонов Наблюдателя (Observer) и Посетителя (Visitor).

Книга Design Patterns содержит раздел на каждый из 23 шаблонов и один или несколько примеров в каждом разделе, обычно с использованием C++ (скорее ограниченные С++) но некоторые реализованы на Smalltalk. (Вы найдете, что это не имеет большого значения, так как вы легко можете перевести концепцию с этих язоков на Java.) Эта книга пересматривает многие шаблоны, рассмотренные в Design Patterns, но с использованием ориентации на Java, так как язык меняет выражение и понимание шаблона. Однако примеры GoF не будут повторены здесь, так как я верю, что можно привести более показательные примеры, дающие тот же объем. Цель состоит в обеспечении вас достаточным пониманием того, что такое шаблоны и почему они так важны.

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

Однако позднее внутренняя интуиция подсказала мне, что полезней организовать шаблоны в терминах проблемы, которую они решают. Я верю, что это тонкое, но важное отличие от пути организации шаблонов Мецкера (Metsker), используемой в Design Patterns Java Workshop (Addison-Wesley 2002), и потому, я надеюсь, что вы в последствии будете способны распознать вашу проблему и найти решение, если шаблон будет организован этим способом.

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

Требования к разработке

Необходима способность к разработке, процессор UML process, Экстремальное Программирование.

Оценка важна? The Capability Immaturity Model:

Wiki Page: http://c2.com/cgi-bin/wiki?CapabilityImMaturityModel
Статья: http://www.embedded.com/98/9807br.htm

Исследование Программирования парами:

http://collaboration.csc.ncsu.edu/laurie/

Тестирование модулей

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

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

Я преподнес и описал стиль кодирования JUnit, который, я рассчитываю на это, является "лучшей практикой" (в основном из-за своей простоты), в книге Thinking in Java, 3-я редакция, глава 15. Этот раздел обеспечивает адекватное введение для любого тестируемого модуля, которые вы найдете связанным с этой книгой (однако, код тестирования модулей было бы ненормально включать в текст этой книги). Когда вы загрузите код этой книги, вы найдете (Вероятно, после 4.9.2003, но не теперь) тестирование модулей наряду с примерами кодов, если это будет возможно.

Расположение тестового кода

(From Bill):

Публичное: в поддиректории test; отдельным пакетом (не включен в jar).

Доступ к пакету: тот же пакет, поддиректорий в пути кода библиотеки (не включен в jar).

Частный доступ: (тестирование белой коробки). Вложенный класс, подключение или добавление JUnit.