пятница, 21 февраля 2014 г.

GoF паттерны на платформе .NET

След. запись: Паттерн Стратегия

Посты серии:

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

Так, когда в начале 90-х на арене программного мира появились паттерны проектирования, многие начали мечтать о том, что благодаря им даже бизнес-пользователи и 1С программисты смогут собирать приложение из готовых кирпичиков. Довольно быстро стало ясно, что этого не случится и начали искать другие подходы, включая программирование через конфигурацию, пламенно воспетую Хантом и Томасом в их «Программисте-прагматике». Затем появились IoC (или DI) контейнеры и начался новый этап создания «слабосвязанных приложений», разбираться с которыми стало еще сложнее, чем прежде благодаря замене «физической» (прямой) связи между компонентами на «логическую» (косвенную).

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

Отношение к паттернам проектирования

Большинство разработчиков ПО сходятся в мысли, что паттерны проектирования – небезынтересная вещь, но, тем не менее, многие относятся к этому инструменту по-разному. Почему так вышло? Когда молодой разработчик сталкивается с новым инструментом, то он изо всех сил старается воспользоваться им по максимуму. В результате, он проходит определенные стадии развития, которые в случае паттернов проектирования могут выглядеть так:

1-я стадия: Ух-ты-Ух-ты-Ух-ты! Я узнал, что такое паттерны! Класс! Когда и где я смогу ими воспользоваться?
2-я стадия: Ух-ты! Я отрефакторил старый код и вместо десяти строк кода заиспользовал 7 паттернов! Я мегакрут!
3-я стадия: ух-ты. Ну, паттерны - это классная штука, но через пару месяцев мой прошлый рефакторинг уже не кажется таким уж классным. Что-то я и сам начал путаться за всеми этими абстрактными фасадированными декораторами, завернутыми в синглтон.
4-я стадия: нет, паттерны – это хорошо, но нужно отталкиваться не от паттернов, а от решаемой задачи и уже исходя из проблемы выбирать подходящие решения. Паттерны – хорошо, но своя голова – на порядок лучше!

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

Какое будет у вас отношение к паттернам при виде классов вроде AbstractSingletonProxyFactoryBean и приложений “Hello, World” следующего вида?

public class HelloWorld
{
   
public static void Main(String
[] args)
    {
       
MessageBody mb = new MessageBody
();
        mb
.Configure("Hello World!"
);
       
AbstractStrategyFactory asf = DefaultFactory.
Instance;
       
MessageStrategy strategy = asf.
CreateStrategy(mb);
        mb
.Send(strategy);
    }
}

ПРИМЕЧАНИЕ
Пример кода взят из обсуждения паттернов проектирования на shashdot.

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

Большинство экспертов в ООП сходятся на мысли, что основным инструментом ООП является не наследование, а инкапсуляция – сокрытие информации. Этой же точки зрения придерживались и авторы “Design Patterns”, советуя предпочитать агрегацию наследованию.

Но несмотря на этот совет, оригинальные диаграммы классов большинства паттернов проектирования поддерживают специализацию путем наследования. Есть паттерны, в которых наследование является неотъемлемой частью самого решения, но ведь есть и такие, в которых наследование является второстепенной характеристикой, предназначенной для получения более гибкого решения. К первой категории относятся Декоратор, Композит, Стратегия, Команда, Шаблонный Метод и другие. Но ведь для таких паттернов, как Строитель, Посредник, Прокси, Фабричный Метод наследование не является обязательным.

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

Фреймворки паттернов

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

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

Для чего нужна еще одна серия постов о паттернах?

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

Паттерны не привязаны к платформе, но их типовая реализация зачастую несколько отличается от языка к языку, иногда из-за технических различий, иногда – из-за культурных. Это не очередная серия постов типа, «а давайте перескажем GoF паттерны, вместе с оригинальными определениями и диаграммами классов». Цель у меня несколько иная.

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

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

Дополнительные источники

10 комментариев:

  1. в список дополнительных источников - www.dofactory.com ;)

    ОтветитьУдалить
    Ответы
    1. Мне показались примеры на dotfactory не убедительными. По словам авторов ресурса посредник, например, используется редко, хотя это не так. Ну и напрягают канонические диаграммы, на которые просто нельзя ориентироваться без полного понимания, когда полная версия паттерна подходит, а когда стоит использовать упрощенную версию, а всего этого я там не увидел:(.

      Удалить
  2. А про элементарные шаблоны проектирования тоже расскажете? Очень интересно, как дошли до того, чтобы ещё больше разобрать шаблоны на "элементарные частицы".

    ОтветитьУдалить
    Ответы
    1. На самом деле цель у меня несколько проще: показать, какое влияние оказывает тот или иной паттерн и как мы с вами его обычно используем, в каком виде и зачем. Ну и как эти паттерны представлены в составе .NET Framework.

      Планов про элементарные шаблоны проектирования никаких не было, но это не значит, что они не появятся;)

      Удалить
    2. А элементарные это какие? Как я понимаю, все шаблоны элементарные, ведь они (ну не всегда, конечно) в идеале не используют никакие другие шаблоны

      Удалить
    3. Я думаю, что имеется ввиду вот это: http://www.amazon.com/Elemental-Design-Patterns-Jason-Smith/dp/0321711920

      Удалить
  3. Хорошее введение :)
    Надеюсь, продолжение не заставит себя ждать, так как необходимость некоторых из шаблонов для меня до сих пор остается сомнительной, хотя то, что они являются классическими, заставляет меня считать, что я что-то упустил

    ОтветитьУдалить
  4. Сергей, уделите внимание типовым примерам использования паттернов. Когда их стоит применять,а когда-нет.

    П.С. заказал http://www.ozon.ru/context/detail/id/20216992 - Патерны Проектирования.
    У вас есть\будет обзор на эту книгу?

    ОтветитьУдалить
    Ответы
    1. Поскольку я уже начал публиковать статьи с описанием паттернов, то уже можно судить о том, насколько я уделил внимание типовым примерам использования. Вроде как уделил, но моя цель была показать паттерны не столько с точки зрения типовых примеров использования, сколько с точки зрения рассмотрения паттерна в контексте классических принципов и подходов ОО-дизайна.

      З.Ы. Фриман и Фриман. Паттерны проектирования.
      З.Ы.Ы. Я использую такой простой хинт для поиска по своему блогу: гугл: "programming stuff + паттерны проектирования". Первая ссылка будет именно указанная статья. Вот пример

      Удалить