
- •Гагарина. Л.Г., Федоров а.Р., Федоров п.А. Введение в архитектуру проектирования программного обеспечения Москва
- •Оглавление
- •5.11. Паттерны grasp
- •Глава 1. Архитектура как форма концептуального существования по
- •1.1. Определения архитектуры по и ее значимость
- •1.2. Место архитектурных решений
- •1.3. Роль архитектурных решений
- •1.4. Архитектурные концептуальные схемы. Определение и ретроспектива
- •Глава 2. Нормативные практики архитектурного описания по
- •2.1. Основные понятия
- •2.2. Содержание стандарта
- •2.3. Представления схемы ieee-1471
- •Глава 3. Рациональный процесс архитектурного моделирования
- •3.1. Архитектурные парадигмы
- •3.2. Примеры архитектурных стилей и моделей
- •Глава 4. Сравнительное сопоставление архитектурных видов
- •4.1. Сопоставление систем видов
- •4.2. Примеры систем видов
- •Языки описания архитектуры
- •Глава 5. Проектирование с учетом будущих изменений.
- •5.1. Что такое паттерн проектирования.
- •5.2. Как решать задачи проектирования с помощью паттернов
- •3. Зависимость от аппаратной и программной платформ.
- •5.4. Порождающие паттерны
- •5.4.1. Паттерн Фабричный метод (Factory Method) - уровень класса
- •Классический вариант фабричного метода, когда интерфейс фабричных методов объявляется в независимом классе-фабрике, а их реализация определяется конкретными подклассами этого класса (Рис. 33).
- •Реализация паттерна Factory Method на основе обобщенного конструктора
- •Void info() {
- •Void info() {
- •Void info() {
- •Int main()
- •V.Push_back( Warrior::createWarrior( Infantryman_id));
- •V.Push_back( Warrior::createWarrior( Archer_id));
- •V.Push_back( Warrior::createWarrior( Horseman_id));
- •Void info() {
- •Void info() {
- •Void info() {
- •Int main()
- •5.4.2. Паттерн Одиночка (Singleton) - уровень объекта
- •Классическая реализация Singleton
- •If(!p_instance)
- •Singleton Мэйерса
- •Улучшенная версия классической реализации Singleton
- •Void initialize( Singleton* p );
- •Void SingletonDestroyer::initialize( Singleton* p ) {
- •If(!p_instance) {
- •Использование нескольких взаимозависимых одиночек
- •Int main()
- •5.4.3. Паттерн Абстрактная фабрика (Abstract Factory) - уровень объекта
- •Пример кода для паттерна Abstract Factory
- •Void info() {
- •Int main()
- •5.4.4. Паттерн Строитель (Builder) - уровень объекта
- •Void info() {
- •Int main()
- •Infantryman
- •Infantryman
- •5.4.5. Паттерн Прототип (Prototype) - уровень объекта
- •Void info() {
- •Void info() {
- •5.4.6. Обсуждение порождающих паттернов
- •5.5. Структурные паттерны
- •5.5.1. Паттерн Адаптер, обертка (Adapter, wrapper)
- •Адаптер объекта применяет композицию объектов.
- •Int main()
- •Void adjust() {} // Настройка датчика (защищенный метод)
- •5.5.2. Паттерн Мост (Bridge)
- •Void log( string & str );
- •Достоинства паттерна Bridge
- •5.5.3. Паттерн компоновщик (Composite)
- •Описание паттерна Composite
- •Virtual void addUnit(Unit* p) {
- •Int main()
- •Virtual CompositeUnit* getComposite() {
- •Void addUnit(Unit* p);
- •Достоинства паттерна Composite
- •Недостатки паттерна Composite
- •5.5.4. Паттерн декоратор (Decorator , wrapper, обертка)
- •Реализация паттерна Decorator
- •Int width, height;
- •5.5.5. Паттерн фасад (Facade)
- •Void submitNetworkRequest()
- •If (_engineer.CheckOnStatus())
- •5.5.6. Паттерн приспособленец (Flyweight)
- •Структура паттерна приспособленец (Flyweight)
- •Icon(char *fileName)
- •Icon *FlyweightFactory::_icons[];
- •5.5.7 Паттерн заместитель (Proxy, surrogate, суррогат)
- •Void draw()
- •Int balance;
- •5.5.8. Обсуждение структурных паттернов
- •5.6. Паттерны поведения
- •5.6.1. ПаттернЦепочка обязанностей (Chain of Responsibility)
- •Void setNext(Base *n)
- •5.6.2. Паттерн Command (команда)
- •Реализация паттерна Command
- •5.6.3. Паттерн Interpreter (интерпетатор)
- •Совместное использование паттернов Interpreter и Template Method
- •Int interpret(char*); // interpret() for client
- •Virtual void interpret(char *input, int &total)
- •Int index;
- •If (!strncmp(input, nine(), 2))
- •Virtual char one(){}
- •Int items[10];
- •Int main()
- •5.6.5. Паттерн Mediator (посредник)
- •Virtual void changed();
- •Void Widget::changed()
- •Int main()
- •5.6.6. Паттерн Memento (хранитель)
- •Int _state;
- •Void static redo()
- •Int main()
- •Integer: 11
- •5.6.7. Паттерн Observer (наблюдатель)
- •Int value;
- •5.6.8. Паттерн State
- •Void setCurrent(State *s)
- •5.6.9. Паттерн Strategy
- •Void compress( const string & file ) {
- •5.6.10. Паттерн Template Method (шаблонный метод)
- •Void a()
- •V.Visit(this);
- •V.Visit(this);
- •V.Visit(this);
- •Int main()
- •5.6.12. Обсуждение паттернов поведения
- •5.7. Дальнейшее развитие идеи паттернов проектирования
- •5.8. Архитектурные системные паттерны
- •5.9. Паттерны управления
- •5.9.1. Паттерны централизованного управления
- •5.10. Паттерны интеграции корпоративных информационных систем
- •5.10.1. Структурные паттерны интеграции
- •5.10.2. Паттерны по методу интеграции
- •5.10.3. Паттерны интеграции по типу обмена данными
- •5.12. Антипаттерны (anti-patterns)
- •Глава 6. Архитектура и характеристики качества
- •6.1. Специфика требований к качеству по
- •6.2. Подход к построению архитектуры с позиций качества
- •6.3. Подходы к оцениванию архитектуры
5.12. Антипаттерны (anti-patterns)
Антипаттерны в противовес практики хорошего программирования — это классы наиболее часто внедряемых плохих решений проблем. Они изучаются, как категория чтобы избежать проблем в будущем, либо распознать отдельные случаи их применения при изучении неработающих систем.
Впервые термин «Антипаттерн» в смысле формальной модели типичного неудачного решение используется в 1996 году Майклом Эйкройдом (англ. Michael Akroyd) на конференции «Object World West Conference», посвященной аспектам объектно-ориентированного программирования [19]. В своей презентации «Антипаттерны: предотвращение неправильного использования объектов», Эйкройд обращал внимание на вредные, но частые программные конструкции, в частности, те, что противоречат принципам ООП. К тому же, для каждой такой конструкции он предлагал эффективную замену.
Частью хорошей практики программированиясчитается программирование без антипаттернов. У программистов, как и у работников любой сферы деятельности, можно выделить типичные ошибки, обусловленные недостаточной базой знаний или отсутствием опыта, сжатыми сроками сдачи проекта, финансовыми ограничениями и прочим [20].
Некоторые различаемые антипаттерны в программировании
Антипаттерны в управлении разработкой ПО
Дым и зеркала (Smoke and mirrors): Демонстрация того, как будут выглядеть ненаписанные функции (название происходит от двух излюбленных способов, которыми фокусники скрывают свои секреты)
Раздувание ПО(Software bloat): Разрешение последующим версиям системы требовать всё больше и больше ресурсов
Функции для галочки: Превращение программы в конгломерат плохо реализованных и не связанных между собой функций (как правило, для того, чтобы заявить в рекламе, что функция есть)
Антипаттерны в проектировании ПО
Инверсия абстракции (Abstraction inversion): Сокрытие части функциональности от внешнего использования, в надежде на то, что никто не будет его использовать
Неопределённая точка зрения (Ambiguous viewpoint): Представление модели без спецификации её точки рассмотрения
Большой комок грязи (Big ball of mud): Система с нераспознаваемой структурой
Бензиновая фабрика (Gas factory): Необязательная сложность дизайна
Затычка на ввод данных (Input kludge): Забывчивость в спецификации и выполнении поддержки возможного неверного ввода
Раздувание интерфейса (Interface bloat): Разработка интерфейса очень мощным и очень сложным для реализации
Волшебная кнопка (Magic pushbutton): Выполнение результатов действий пользователя в виде неподходящего (недостаточно абстрактного) интерфейса. Например, в системах типа Delphi это написание прикладной логики в обработчиках нажатий на кнопку
Перестыковка (Re-Coupling): Процесс внедрения ненужной зависимости
Дымоход (Stovepipe system): Редко поддерживаемая сборка плохо связанных компонентов
Состояние гонки (Race hazard, Race condition): непредвидение возможности наступления событий в порядке, отличном от ожидаемого
Мышиная возня: Неоправданное создание множества мелких и абстракных классов для решения одной конкретной задачи более высокого уровня
Висящие концы: Интерфейс, большинство методов которого бессмысленны и реализуются "пустышками"
Золушкина туфелька: Попытка "натянуть" на объект уже имеющийся малоподходящий по смыслу интерфейс, вместо создания нового.
Членовредительство (Mutilation): Излишнее "затачивание" объекта под определенную очень узкую задачу таким образом, что он не способен будет работать с никакими иными, пусть и очень схожими задачами.
Антипаттерны в объектно-ориентированном программировании
Базовый класс-утилита (BaseBean): Наследование функциональности из класса-утилиты вместо делегирования к нему
Вызов предка (CallSuper): Для реализации прикладной функциональности методу класса-потомка требуется в обязательном порядке вызывать те же методы класса-предка
Ошибка пустого подкласса (Empty subclass failure): Создание класса (в Perl), который не проходит «проверку пустоты подкласса» («Empty Subclass Test») из-за различного поведения по сравнению с классом, который наследуется от него без изменений
Божественный объект (God object): Концентрация слишком большого количества функций в одной части системы (классе)
Объектная клоака (Object cesspool): Переиспользование объектов, находящихся в непригодном для переиспользования состоянии
Полтергейст (Poltergeist): Объекты, чьё единственное предназначение — передавать информацию другим объектам
Проблема йо-йо (Yo-yo problem): Чрезмерная размытость сильно связанного кода (например, выполняемого по порядку) по иерархии классов
Одиночество (Singletonitis): Неуместное использование паттерна одиночка
Приватизация (Privatisation): Сокрытие функциональности в приватной секции (private), что затрудняет его расширение в классах-потомках
Френд-зона (Friend zone): Неуместное использование дружественных классов и дружественных функций в языке с++
Каша из интерфейсов (Interface soup): Объединение нескольких интерфейсов, разделенных согласно принципу изоляции интерфейсов (Interface segregation), в один.
Антипаттерны в программировании
Ненужная сложность (Accidental complexity): Внесение ненужной сложности в решение
Действие на расстоянии (Action at a distance): Неожиданное взаимодействие между широко разделёнными частями системы
Накопить и запустить (Accumulate and fire): Установка параметров подпрограмм в наборе глобальных переменных
Слепая вера (Blind faith): Недостаточная проверка корректности исправления ошибки или результата работы подпрограммы
Лодочный якорь (Boat anchor): Сохранение более не используемой части системы
Активное ожидание (Busy spin): Потребление ресурсов ЦПУ (процессорного времени) во время ожидания события, обычно при помощи постоянно повторяемой проверки, вместо того, чтобы использовать асинхронное программирование (к примеру, систему сообщений или событий)
Кэширование ошибки (Caching failure): Забывать сбросить флаг ошибки после её обработки
Воняющий подгузник (The Diaper Pattern Stinks): Сброс флага ошибки без её обработки или передачи вышестоящему обработчику
Проверка типа вместо интерфейса (Checking type instead of membership, Checking type instead of interface): Проверка того, что объект имеет специфический тип в то время, когда требуется только определённый интерфейс
Инерция кода (Code momentum): Сверхограничение части системы путём постоянного подразумевания её поведения в других частях системы
Кодирование путём исключения (Coding by exception): Добавление нового кода для поддержки каждого специального распознанного случая
Таинственный код (Cryptic code): Использование аббревиатур вместо мнемоничных имён
Жёсткое кодирование (Hard code): Внедрение предположений об окружении системы в слишком большом количестве точек её реализации
Мягкое кодирование (Soft code): Патологическая боязнь жёсткого кодирования, приводящая к тому, что настраивается всё что угодно, при этом конфигурирование системы само по себе превращается в программирование
Поток лавы (Lava flow): Сохранение нежелательного (излишнего или низкокачественного) кода по причине того, что его удаление слишком дорого или будет иметь непредсказуемые последствия
Волшебные числа (Magic numbers): Включение в алгоритмы чисел без объяснений их смысла
Процедурный код (Procedural code): Когда другая парадигма является более подходящей
Спагетти-код (Spaghetti code, иногда «макароны»): Код с чрезмерно запутанным порядком выполнения
Лазанья-код (Lasagnia code, или "лук" (onion)): Использование неоправданно большого количества уровней абстракции
Равиоли-код (Ravioli code, или "пельмени"): Объекты настолько "склеены" между собой, что практически не допускают рефакторинга
Мыльный пузырь (Soap bubble): Объект, инициализированый мусором, максимально долго притворяется, что содержит какие-то данные
Мютексный ад (Mutex hell): Внедрение слишком большого количества объектов синхронизации между потоками
Сохранение или смерть (Save or die): Сохранение изменений конфигурации на жесткий диск лишь при завершении приложения; приводит к тому, что в случае отказа в программе эти данные будут утеряны
Методологические антипаттерны
Программирование методом копирования-вставки (Copy and paste programming): Копирование (и лёгкая модификация) существующего кода вместо создания общих решений
Золотой молоток (Golden hammer): Сильная уверенность в том, что любимое решение универсально применимо. Название происходит от поговорки «когда в руках молоток, все проблемы кажутся гвоздями»
Фактор невероятности (Improbability factor): Предположение о невозможности того, что сработает известная ошибка
Преждевременная оптимизация (Premature optimization): Оптимизация на основе недостаточной информации
Изобретение колеса/велосипеда (Reinventing the wheel): Создание с нуля, вместо использования готового решения
Изобретение квадратного колеса (Reinventing the square wheel): Создание плохого решения, когда существует хорошее известное решение
Самоуничтожение (Self-destruction): Фатальная ошибка либо нестандартное поведение программы, приводящая к отказу в обслуживании, возникшая вследствие другой менее серьёзной ошибки. Например, при возникновении ошибки, приложение начинает очень быстро и много писать в лог, вследствие чего заканчивается место на жёстком диске быстрее, чем это обнаружит мониторинг.
Два тоннеля: Вынесение новой функциональности в отдельное приложение вместо расширения уже имеющегося. Чаще всего применяется, когда по каким-либо причинам (в основном, при нехватке времени либо нежелании менеджмента) внесение изменений в уже имеющийся код требует больших затрат, чем создание нового. При этом у клиента в конечном итоге работают два приложения, запускаясь одновременно либо попеременно друг из друга.
Коммит-убийца (Commit assasin): Внесение отдельных изменений в систему контроля версий без проверки влияния их на другие части системы. Как правило, после подобных коммитов работа коллектива парализуется на время исправления проблем в местах, которые ранее работали безошибочно.
Антипаттерны управления конфигурацией
Ад зависимостей (Dependency hell), на платформе Microsoft Windows также называется "DLL-ад", "DLL hell"): Разрастание графа взаимных зависимостей программных продуктов и библиотек, приводящее к сложности установки новых и удаления старых продуктов. В сложных случаях различные установленные программные продукты требуют наличия разных версий одной и той же библиотеки. В наиболее сложных случаях один продукт может косвенно потребовать сразу две версии одной и той же библиотеки.
Некоторые организационные антипаттерны
Аналитический паралич (Analysis paralysis): неоправданно большие затраты на анализ и проектирование. Часто приводит к закрытию проекта до начала его реализации;
Дойная корова (Cash cow): когда при наличии продукта, приносящего выгоду без существенных вложений не вкладываются средства в развитие и разработку новых продуктов;
Продолжительное устаревание (Continuous obsolescence): выделение непропорционально больших усилий на портирование системы в новые окружения;
Сваливание расходов (Cost migration): перенос расходов на проект к уязвимому отделу или бизнес-партнёру;
Ползущий улучшизм (Creeping featurism): добавление новых улучшений в ущерб суммарному качеству системы;
Разработка комитетом (Design by committee): разработка проекта без централизованного управления, либо при некомпетентном руководстве;
Неуёмная преданность (Escalation of commitment): продолжение реализации решения после того, как доказана его ошибочность;
Я тебе это говорил (I told you so): игнорирование мнения эксперта
Управление основанное на числах (Management by numbers): излишнее внимание к численным показателям, либо имеющим очень косвенное отношение к управляемой системе, либо сложным для получения;
Драконовские меры (Management by perkele): неоправданно жесткий стиль управления;
Управление грибами (Mushroom management): недостаточное информирование работников о выполняемой работе;
Расползание рамок (Scope creep): потеря контроля над разрастанием проекта;
Замыкание на продавце (Vendor lock-in): жёсткая привязка к поставщику;
Тёплое тело (Warm body): человек, чей вклад в проект под сомнением;
Единственный знающий человек (Single head of knowledge, SHOK): когда жизненно важными для проекта сведениями или навыками обладает только один человек в команде; с его уходом работа останавливается;
Рыцарь на белом коне (Knight in shining armor, KISA): когда на сцене появляется человек, который пытается починить всё, не сообщая никому, что он сделал и почему.
Шуточные антипаттерны
Паблик Морозов: Класс-потомок, созданный в соответствии с этим антипаттерном, выдает по запросу все данные класса-предка, независимо от степени их сокрытия. Название данного антипаттерна — это каламбур, основанный на созвучии ключевого слова public (паблик), часто означающего открытый доступ к методам и полям класса в объектно-ориентированных языках программирования, и имени пионера-героя Павлика Морозова, выдавшего, по утверждению советской пропаганды, следственным органам своего отца, имевшего преступные связи с кулаками.