Как-то недавно я наткнулся на статью под названием Читай код, где автор доказывал важность такого скилла, как умение читать чужой код:
Создать свою структуру и пришлепать ее сбоку может любой дурак. Квалифицированный инженер-программист (с упором на первом слове, не путать с “программером”) умеет проводить анализ “чужой” подсистемы, восстановит мысль и идею автора, сможет мысль автора развить, продолжить ее, и эффективно решить свою задачу в рамках чужого подхода к проблеме. Все это – работая с кодом. Это отличительная компетенция архитектора, высший уровень инженерного мастерства. И это имеет весьма отдаленное отношение к “рефакторингу”.
Тогда у меня возникла идея провести эксперемент:
- Взять некоторый известный паттерн проектирования.
- C одной стороны показать, как выглядит код до применения паттерна.
- Рядом же, с другой стороны, показать как выглядит этот же код с применением паттерна.
- Попытаться понять, что этот код делает и как применяется паттерн.
- Сравнить такой вариант описания паттернов с традиционным подходом “UML-диаграмма, словесное объяснение, пример использования”.
Я заметил, что мне легче понять, что делает программа, увидев ее код, а не услышав или прочитав ее описание. С паттернами это чувствуется очень сильно. От такой шпаргалки у меня в глазах рябит:

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

Описание паттерна взято из Википедии. А теперь перейдем ко второму способу объяснения.
Подход «Читай код, сука»

Итак, Вопрос
Что лучше? Что легче для понимания? Подход «Читай код, сука» сработал или нет?
Из примера совершенно очевидно, что легче для понимания код, причем – код слева, без паттерна :). Делает ровно то же самое, и в несколько раз короче. :) А раз он делает то же самое – зачем платить больше?
Самое смешное, на практике все ровно то же самое – искусственно воткнутый паттерн затрудняет читабельность на ровном месте, ибо код раздувает, но никакого отношения к сути задачи не имеет. Соответствено, превентивно, на вырост воткнутые в код паттерны являются мусором, увеличивающим затраты на поддержку, который хуже ранней оптимизации.
Все дело не в том, что паттерн плох, а в том, что в коде слева не обозначено проблемы, которую решает код справа. Хороший дизайн в первую очередь проще для понимания и дешевле при внесении изменений. Здесь этого не наблюдается. Код без паттерна просто обязан демонстрировать необходимость его применения, и необходимость что-то с собой сделать. Здесь – код слева прост и самодостаточен, и ничего с ним делать не надо. Из примера видно одно – немотивированное усложнение кода в случае с паттерном.
— Gaperton · июл 31, 00:09 · #
Да, конечно код слева проще :) Согласен, что очень важно знать, когда и где следует применять паттерны. Но этому уже гораздо сложнее научить, как мне кажется. Здесь же, конечно, просто иллюстрация, как паттерн выглядит. Как эту иллюстрацию использовать – дело девелопера.
Это связано прежде всего, по моему мнению, с тем, что паттерны модульны. Модульны в правильном смысле. Как гандоны. Презерватив можно использовать по назначению. А можно хранить в нем спички в походах или наливать в него воду, чтобы бросать на голову прохожим. Когда и где использовать презерватив выбирает сам человек. То же и с паттернами. Есть некоторые советы, когда и где их использовать, но люди этим не ограничиваются. В этом примере девелепер в декораторе отбивает сообщение звездочками, а мог бы писать логи или отправлять копию на какой-либо адрес. Можно использовать декоратор для того, чтобы спрятать подробности коннекта с базой данных. А можно кешировать в нем результаты выполнения функций.
Все вышеперечисленные задачи можно решить и без паттерна. И, конечно, нужен и умение выбрать правильный путь. А это не так и просто.
— iobit · июл 31, 09:18 · #
Использование паттернов увы из той оперы, которую не показать 2 строчками кода. Так что выигрыш от диаграмм получается двойной: не зависят от языка (не всегда актуально), позволяют явно выделить суть (а если она еще не ясна). Но естественно, что диаграммы надо уметь читать и писать.
— Serger · июл 31, 10:32 · #
> Да, конечно код слева проще :) Согласен, что очень важно знать, когда и где следует применять паттерны. Но этому уже гораздо сложнее научить, как мне кажется. Здесь же, конечно, просто иллюстрация, как паттерн выглядит. Как эту иллюстрацию использовать – дело девелопера.
Единственный смысл “дизайна” – удешевить внесение изменений в код, больше дизайн нафиг не нужен, и отдельно от кода он бесполезен. Хороший не тот дизайн, который построен на паттернах, а тот, код-результат которого проще модифицировать (обычно, он компактнее, и не содержит ничего лишнего). Паттерны – это не то, что надо сознательно “втыкать” в дизайн, чтобы он стал хорошим (будет наоборот) – это устойчивые конфигурации, которые встретятся в хорошем дизайне. Это не одно и то же.
Вы хотели сравнить, как работает подход “читай код” в случае с паттернами? Для этого надо слева привести пример кода, которому отсутствие паттерна мешает, а не помогает. Плохой пример. Вы просто описываете в коде, как паттерн выглядит. А надо еще описать проблему, которую он решает, в виде кода слева, без паттерна. И человеку, глядя в код должно быть очевидно, что код слева “хуже”, чем код справа, в нем проблема.
Отличное упражнение, кстати. Я бы сказал, только тот, кто в состоянии это сделать, на самом деле понимает паттерн.
— Gaperton · июл 31, 11:03 · #
Согласен. Так было бы значительно лучше. Я об этом думал, но было лень придумывать пример. Тут как говорил Serger, “использование паттернов из той оперы, которую не показать 2 строчками кода”. Пусть уже это останется на самостоятельное изучение :)
Честно говоря, я надеялся, что из кода в правой части уже будет понятно, какую функцию он выполняет и для чего. Конечно, с обозначенной проблемой слева это было бы проще.
— iobit · июл 31, 11:30 · #
Я не увидел в примере справа паттерна в упор :)))
Где в нём “динамическое подключение дополнительного поведения к объекту”???
Здесь просто создан подкласс MailSenderDecorator() класса MailSender()“с целью расширения функциональности”. Как я понял для MailSenderDecorator() и MailSender() должен быть как минимум общий интефейс(в виде абстактного класса)…
Я пока не знаток паттернов, но знаю, что тема эта требует немало времени и практики для понимания. И она имеет смысл для разработчиков программных систем(не программистов ради программирования, а инженеров в определённой области), а не для широкорапространённых разработчиков вроде PHP-MySQL , которые используют программные системы, возможно построенные с помощь опыта других инженеров(а опыт этот и есть паттерны – методики решения стандартных, много раз повторяющихся в практике ООП, задач).
И ещё. О какой модульности паттернов можно говорить в принципе???
А если паттерн реализован не как модуль???
Построение модульных и компонентных программ – это отделная тема, кстати.
Модульность программы оределяется в первую очередь языком программирования.
Java не явлется чистым модульным языком(это гибрид нескольких техник программированния) хотя бы потому, что здесь class выпоняет больше роль составного типа, в котором просто описаны переменные(члены класса) и функции(методы), чем модуля. Сам по себе класс ничего не делает.
На роль модуля в Java может претендовать только класс с “методом” main(), в котором и происходит
вся работа…
P.S. Без претензий на роль абсолютной истины, т.к. и сам в начале пути :)
— chip · сен 2, 00:37 · #
Ага. Все правильно. Естественно что должен был общий интерфейс, чтобы позволить влаживание декораторов друг в друга и “динамическое подключение”. Это я напутал и написал совсем не то. Просто не хотелось переделывать и добавлять интерфейс. Можно сказать, что тут выражена “идея декорирования” :) Реализация этой идею в шаблоне Декоратор несколько другая, как вы и заметили.
На счет модульных и компонентных программ не понял. В чем между ними разница? Еще я не знаю, что имеется в виду под понятием “модульный язык”. Думаю, надо наши понятия как-то прояснить.
Когда я говорил о том, что паттерны модульны, я имел ввиду, что они подходят для решения разных задач. В этом и состоит их модульность. Пример с презервативами – еще одна иллюстрация модульности.
— iobit · сен 2, 17:15 · #
Просто много толкований модульности и компонентности. Сейчас эти термины повсеместно используются для рекламно-коммерческих целей, не имея ничего общего с самим их техническим смыслом. Я много интересовался вопросами надёжного программирования и программирования задач реального времени(жаль, что практики было мало). В таких задачах используются языки Pascal, Modula, Oberon, Ada. Ни одна ракета или атомный реактор или прибор для искусственного дыхания не управляются системами, постренными на фигурно-скобочных языках(исключение только ОСРВ QNX, построенная на C, но со строгим использованием теории построения вычислительных систем)…
В таких языках вероятность допущения ошибки программистом сведена к минимуму… через строгую статическую типизацию, например.
Возможно, для понимания вопроса модульности и компонентности необходимо обратится к истокам, откуда эти понятия и произошли.
Предлагаю для начала(чтобы “наши понятия как-то прояснить”) след. ссылки
http://ru.wikipedia.org/wiki/Компонентно-ориентированное_программирование
http://www.inr.ac.ru/~info21/info/qtocop.htm
а уже на основании вышеприведенных ссылок можно смотреть сюда(если сделать наоборот, то получиться эффект “пирамиды стоящей вверх основанием” или “дома на песке”, чем сейчас и занимается большинство профессиональных программистов :)))
http://en.wikipedia.org/wiki/Modular_programming
http://en.wikipedia.org/wiki/Software_componentry
Ещё рекомендую заглянуть сюда http://is.ifmo.ru/ – учиться программированию надо здесь по моему мнению.
P.S. http://is.ifmo.ru/reflections/algorithms/
— chip · окт 12, 18:52 · #
Не все прочитал, но отвечу подробнее, когда будет больше времени. Сейчас адаптируюсь к новой работе и нужно изучать кучу нового. Но спасибо за ссылки – обязательно просмотрю.
— iobit · окт 14, 21:35 · #
Нашёл отличную статью о “модульности” и “компонентности”!
Настоятельно рекомендую!
http://www.oberoncore.ru/programming/gubanov
— chip · ноя 24, 12:54 · #
>> Тогда у меня возникла идея провести эксперемент:
>> 1. Взять некоторый известный паттерн проектирования.
>> 2. C одной стороны показать, как выглядит код до применения паттерна.
>> 3. Рядом же, с другой стороны, показать как выглядит этот же код с применением паттерна.
>> 4. Попытаться понять, что этот код делает и как применяется паттерн.
>> 5. Сравнить такой вариант описания паттернов с традиционным подходом “UML-диаграмма, словесное объяснение, пример использования”.
Рекомендую Вам почитать книгу Джошуа Кериевски “Refactoring to Patterns” (в русском переводе “Рефакторинг с использованием шаблонов”), у него очень похожая методика:
http://www.ozon.ru/context/detail/id/2909721/
— inferno · апр 21, 10:07 · #