Существуют паттерны натуральные, прошедшие долгий путь эволюционного развития, а есть и другие, вроде бы и такие же, но модифицированные, улучшенные — местами до неузнаваемости. Впрочем, название и внешняя схожесть чаще остаются. А вот дальше начинается магия. Уж не знаю, то ли это я такой везучий, то ли жизнь не так проста, как хочется думать, но встречаются мне постоянно вот эти — модифицированные паттерны. В данной статье будет рассмотрено несколько таких примеров и сделана попытка восстановить образ
Одиночка (Singleton) M
М — это «модифицированный». Генно-модифицированный… Итак, знакомьтесь, первый наш герой — одиночка. Казалось бы, этот паттерн слишком прост, чтобы быть улучшенным. Вот что-то типа такого
class MySingleton
{
public MySingleton* Instance();
};
Что же может подтолкнуть к его модификации? Не расширении функциональности — тут простор есть (поддержка заданного количества объектов, многопоточность). Пожалуй, сюда можно добавить больше объектно-ориентированных технологий! А то ведь ни тебе наследования ни полиморфизма. Да и инкапсуляция нарушена, подумать только — возвращаемый тип объекта зашит в интерфейсе! В С++ можно, конечно, воспользоваться шаблонами, но это все равно не ООП, а значит проблемы не решает. Да и как быть программистам на других языках?
ООП без абстрактного интерфейса — не ООП. И проталкивается Instance вверх по иерархии наследования, попутно ее порождая. Возвращать теперь он будет ссылку на экземпляр абстрактного класса, который не содержит всех нужных методов, но тип ведь всегда можно привести… от родительского к дочернему. Может показаться, что это небезопасно, однако наш герой не планирует наследовать от этого интерфейса что-либо еще. Так что все надежно.
MySingleton(Singleton::Instance())->meSingletonMethod()
Команда (Command) M
Дизайн — не дизайн без команд. Они позволяют существенно снизить уровень связанности компонентов, делая систему более масштабируемой. Также программист избавляется от необходимости реализовывать бесконечные ветвления или огромные case'ы. Кроме того, открывается еще ряд возможностей, типа создания пакетов команд, последовательной отмены операций и возможности параметризации действиями, но нашего героя это интересует меньше.
Ведь не решены базовые серьезные проблемы. Во-первых, в результате широкого использования команд практически полностью игнорируется такое мощное и выразительное средство языка, как оператор case. Представьте себе водителя, который рассказывает, как ездил какое-то время, вообще не пользуясь педалью тормоза. Псих — не иначе. С программированием все аналогично. Если оператор существует, им нужно пользоваться, ведь создатели языков — умные люди. Но выход есть! Принимается первое ключевое решение — пусть все главные объекты программы получают все команды. Для выбора необходимого действия воспользуемся оператором case (ура!), ключом же выбора послужит специальное поле команды.
Преимущества очевидны. Во-первых, одна команда может инициировать разные операции в зависимости от объекта-получателя. Это уже очень гибко. К тому же получатели смогут фильтровать команды, отправляя избранные другим объектам. Значит, объект даже не получит ненужную для себя (с точки зрения источника) команду. Хороший признак правильного решения — кроме красивого дизайна в качестве дополнительного бонуса получаем заметную оптимизацию.
Следующий плюс — победа над вторым недостатком команд. Речь идет о слабой связанности. Я думаю, все слышали притчу о венике, который прочен, лишь будучи цельным. Прутик за прутиком же он очень легко ломается. А ведь устойчивость системы важна ничуть не меньше ее гибкости. Необходимость же явным образом передавать команды от объекта к объекту значительно увеличивает прочность системы, являясь для нее своеобразными ребрами жесткости.
Кроме того, предлагаемая модификация делает логику системы очевидной. Каждый метод содержит четкий список возможных реакций на команды и явные направления распространения управляющих сигналов.
Однако здесь очень важно соблюдать баланс. Слишком слабая связанность была побеждена, но и слишком сильная в свою очередь — опасна. Поэтому даже если один объект содержит прямую ссылку на другой, лучше воздержаться от прямого вызова его методов, передавая вместо этого нужную команду!
В конце концов имеем идиллическую картину. Все объекты общаются друг с другом исключительно через команды, даже если имеют прямые ссылки друг на друга. Если в каком-то объекте есть логика обработки команды, совсем не обязательно, что она дотуда дойдет — возможно сигнал будет отфильтрован по пути. Но зато все гордо зовется «Команды».
Модель-представление-контроллер (MVC) M
Но в конце концов, команды и одиночки — лишь атрибуты объектно-ориентированного дизайна. MVC же является скелетом, на котором держится функциональность всей системы. Остановимся на варианте тонкой модели. Когда основная логика сосредотачивается в контроллерах. В одном контроллере чаще всего, конечно. Представление же берет на себя обязанности пользовательского ввода и вывода информации.
Чтобы стимулировать пользователей работать с моделями, команды параметризуются исключительно ими. Своя модель для каждой команды — что может быть прозрачнее…
Что на выходе? Приложение, при первом взгляде производящее основательное впечатление. Куда ни плюнь — попадешь в паттерн. Точнее его название. Не хотелось думать, какие части системы владеют другими, не проблема — сделаем Фасад, который будет справочником, возвращающим все и вся. Не получилось логически разделить логику работы — пусть всей ей занимается волшебный суперконтроллер. Проблем быть не может, ведь он часть очень популярного и хорошо зарекомендовавшего себя паттерна… Откуда оно все берется? Ладно бы еще человек не слышал об этих вещах, я верю, что хорошо можно сделать и без прямого использования паттернов. Но выходит, читал ведь, запомнил и применил… как смог… В связи с этим хотелось бы провести опрос среди тех, кто вынужден работать с унаследованными системами.
Автор: