Принципы и шаблоны проектирования GRASP / SOLID
Тетерятников Алексей
Романенко Константин
Цели:
1. Изучить принципы SOLID и шаблоны GRASP
2. Объяснить на примере котиков
Серия докладов
Вспомним 70-е...
Кристофер Александер
Архитектурные шаблоны
Шаблоны программирования
Время | Веха | Имена |
---|---|---|
середина 1980-х | шаблоны программирования | Кент Бек, Уорд Каннингем |
1988-1994 | шаблоны "Банды четырех" (GoF) | Эрих Гамма Ричард Хелм Ральф Джонсон Джон Влиссидес [ozon.ru] |
Принципы проектирования
Время | Веха | Имена |
---|---|---|
1997 | принципы и шаблоны GRASP | Крэг Ларман [ozon.ru] |
2002 | принципы SOLID |
Роберт Мартин |
Крэг Ларман
1970-е разработчик APL, Lisp, Smalltalk
1997 принципы GRASP
2005 соавтор большого масштабируемого Scrum (Large-Scale Scrum)
Консультант и автор в области разработки ПО
Автор известных книг:
- Чистый код
- Чистая архитектура
- Принципы, паттерны и методики гибкой разработки на языке C#
Роберт Мартин
Дядя Боб (Uncle Bob)
Бертран Майер
- Автор принципа OCP
- Создатель языка программирования Эйфель.
- C 2015 года преподаватель в университете Иннополис (Иннополис, Республика Татарстан, Россия).
Автор известных книг:
- Почувствуй класс
- Объектно-ориентированное конструирование программных систем
Принципы Проектирования SOLID
А что такое
"Принципы Проектирования"?
Принцип проектирования – это методологическое правило, которое выражает общий взгляд на разработку ПО.
Принципы проектирования по Б. Майеру
Принцип проектирования – это методологическое правило, которое выражает общий взгляд на разработку ПО.
Принципы проектирования по Б. Майеру
Хороший принцип является одновременно:
Абстрактным -
принцип должен описывать универсальное правило, а не конкретную практику.
Опровергаемым -
у разумного человека должна быть возможность не согласиться с принципом.
SOLID как устранение недостатков ПО
- Жесткость
- Хрупкость
- Косность
- Вязкость
- Ненужная сложность
- Ненужные повторения
- Непрозрачность
(SRP) Принцип единственной ответственности
1. Декомпозиция на ответственности
Уход за котом
- Кормить
- Гигиеничесие процедуры
- Развлекать
Задаемся вопросом:
"что делает класс?"
1. Декомпозиция на ответственности
Описывается порядок ухода за котом -
ответственность одна?
Задаемся вопросом:
"что делает класс?"
Уход за котом
- Кормить
- Развлекать
- Гигиеничесие процедуры
2. Причина для изменений должна быть только одна
Уход за котом
- Кормить
- Развлекать
- Гигиеничесие процедуры
2. Причина для изменений должна быть только одна
Наполнить миску
Приготовить
Проверить запасы
Помыть
Расчесать
{
Светить лазером
Бросать мяч
}
{
{
}
}
Уход за котом
- Кормить
- Развлекать
- Гигиеничесие процедуры
3. Изменяемые вместе элементы должны быть рядом
Кормить
Процедуры
Развлекать
Помыть
Расчесать
Уход за котом
Светить лазером
Бросать мяч
Наполнить миску
Приготовить
Проверить запасы
Что должно храниться в одном классе?
Изменения одного элемента обязательно приведет к изменению других
Кормить
Наполнить миску
Приготовить
Проверить запасы
Что должно храниться в одном классе?
Изменения одного элемента обязательно приведет к изменению других
Кормить
Наполнить миску
Приготовить
Проверить запасы
Нет причин изменять элемент отдельно
Что должно храниться в одном классе?
Изменения одного элемента обязательно приведет к изменению других
Класс имеет высокую связанность
Кормить
Наполнить миску
Приготовить
Проверить запасы
Нет причин изменять элемент отдельно
Итоги SRP
1. Код разбивается на мелкие модули;
2. Четкое понимание, что делает модуль;
3. При совместной разработке упрощает процесс слияния кода.
(LSP) Принцип подстановки Барбары Лисков
(LSP) Принцип подстановки Барбары Лисков
- Должна быть возможность вместо базового типа подставить любой его подтип.
- Логика родителя не должна нарушаться.
Поступают новые требования
Поступают новые требования
Базовый кот из программы
"уход за котом"
Шкаф
Подними кота на шкаф
Подклей угол шкафа (он его сгрыз)
Жди пока кот выспится
Базовый Кот
Спальня
Спать()
Базовый кот из программы
"уход за котом"
Шкаф
Подними кота на шкаф
Подклей угол шкафа (он его сгрыз)
Жди пока кот выспится
Базовый Кот
Спальня
Спать()
Ошибки в наследовании
Ошибки в наследовании
Невозможно установить правильность модели, рассматриваемой изолированно
Как избегать ошибок при наследовании?
- Программирование по контракту
Как избегать ошибок при наследовании?
- Программирование по контракту
- Тесты
Предусловия
Постусловия
Программирование по контракту
Предусловия
Постусловия
Программирование по контракту
Кот:
Рост: <50 см;
Пушистый;
Вес: <20 кг.
Предусловия
Постусловия
Программирование по контракту
Кот полон энергии
Базовый Кот
Спальня
Спать()
Кот:
Рост: <50 см;
Пушистый;
Вес: <20 кг.
Программирование по контракту
Правило для производных классов:
- Не усиливать Предусловия
- Не ослаблять Постусловия
Итоги LSP
1. Согласует поведение базового и производного класса;
2. Гарантирует корректность работы ПО при замене базового класса производным.
(ISP) Принцип разделения интерфейсов
Клиенты не должны вынужденно зависеть от методов, которыми не пользуются
(ISP) Принцип разделения интерфейсов
Интерфейс кота
Имя
Жирные интерфесы
Уход за котом
Жирные интерфесы
Выставка котов
Уход за котом
+
Интерфейс кота
Титул
Имя
Назначить титул (v)
Жирные интерфесы
Выставка котов
Ветеринарная клиника
Уход за котом
Интерфейс кота
История болезней
Титул
Имя
Назначить титул (v)
Внести болезнь (v)
+
+
Разделение интерфейса в зависимости от клиентов
Выставка котов
Ветеринарная клиника
Уход за котом
Интерфейс кота
Имя
Интерфейс болеющего
Имя
История болезней
Внести болезнь (v)
Интерфейс Конкурсанта
Имя
Титул
Назначить титул (v)
Что делать если в интерфейсе есть itemID, а он вам не нужен?
Пример из Flex интерфейса Эталона
Пример из Flex интерфейса Эталона
Создаем переменную "uselessVarForInterfaceImplementation"
Итоги ISP
1. Код разбивается на мелкие модули;
2. уменьшает связанность между классом и его клиентом (low coupling).
(DIP) Принцип инверсии зависимости
(DIP) Принцип инверсии зависимости
1. Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций.
2. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Типичный случай:
Зависимости исходного кода следуют за потоком управления
Инверсия зависимости
- Не должно быть переменных, в которых хранятся ссылки на конкретные классы.
- Не должно быть классов, производных от конкретных классов.
-
Не должно быть методов, переопределяющих метод, реализованный в одном из базовых классов
Модули должны зависеть от абстракции
Коробка
Спальня кота
Постели подушку на дно
Подклей коробку (он её сгрыз)
Жди пока кот выспится
Базовый Кот
Спальня
Спать()
Коробка
Шкаф
Постели подушку на дно
Подклей коробку (он её сгрыз)
Жди пока кот выспится
Подними кота на шкаф
Подклей угол шкафа (он его сгрыз)
Жди пока кот выспится
Спальня кота
Базовый Кот
Спальня
Спать()
Абстрактное спальное место
Шкаф
Домик для кота
Коробка
Спальня кота
Подготовка
Восстановление
Жди пока кот выспится
Базовый Кот
Спальня
Спать()
Итоги DIP
1. Контроль над зависимостями в исходном коде;
2. Позволяет сделать модули автономными, переиспользуемыми;
(OCP) Принцип
открытости/закрытости
Программные сущности должны быть:
- открыты для расширения,
- закрыты для модификации
(OCP) Принцип
открытости/закрытости
Разделять модули, которые изменяются по разным причинам
SRP
Как достигается?
DIP
+
Выстраивать зависимости таким образом, чтобы не было зависимостей от наиболее изменяемых модулей
Абстрактное спальное место
Шкаф
Домик для кота
Коробка
Спальня кота
Подготовка
Восстановление
Жди пока кот выспится
Базовый Кот
Спальня
Спать()
Примеры OCP в СИС
- QueryObject
- SOU.ETA.View.Reorganize.Reorganizer
- SOU.System.Configuration
Новое поведение импорта / экспорта задается добавлением нового наследника ConfigItem
Реорганизация представлений
- .
Примеры OCP в СИС
Итоги SOLID
Принципы нацелены на создание структур, которые:
- Легко изменяются по требованиям
- Просты и понятны
- Повторно используемые
GRASP: general responsibility assignment software patterns
общие шаблоны распределения обязанностей
Шаблон - именованная пара "Проблема - Решение"
RDD: responsibility driven design
Проектирование на основе обязанностей
GRASP: классификация шаблонов по Ларману
Основные шаблоны
Дополнительные шаблоны
- Information expert (Информационный эксперт)
- Creator (создатель)
- High Cohesion (Высокая связность)
- Low Coupling (Низкое зацепление)
- Controller (Контроллер)
- Polymorphism (Полиморфизм)
- Indirection (Перенаправление)
- Pure Fabrication (чистая выдумка, синтетика)
- Protected Variations (Устойчивость к изменениям)
GRASP: классификация доклада
Шаблоны - принципы
- High Cohesion (Высокая связность)
- Low Coupling (Низкое зацепление)
- Protected Variations (Устойчивость к изменениям)
- Polymorphism (Полиморфизм)
GRASP: классификация доклада
- Information expert (Эксперт)
- Creator (Создатель)
- Controller (Контроллер)
Шаблоны - роли
- Indirection (Перенаправление)
- Pure Fabrication (чистая выдумка, синтетика)
Cohesion & Coupling
GRASP: high cohesion / low coupling
Cohesion - Связность (Сцепленность)
the act or state of sticking together tightly
Coupling - Зацепление (Связывание, Сопряжение)
the act of bringing ... together
Низкое зацепление (связывание)
GRASP: low coupling
Как добиться незначительного влияния изменений и возможности повторного использования ?
Минимизировать количество данных о других элементах!
GRASP: low coupling
Минимазация зацепления: атрибуты
- Избавиться от атрибутов другого типа (кроме стандартных)
Катавасия
датаРождения: DateTime
кличка: String
ночноеЗрение: NightVision
GRASP: low coupling
Минимизация зацепления: методы
- Не использовать атрибуты другого типа (кроме стандартных)
- Не вызывать службы классов другого типа
- Не использовать другой тип для параметров, локальной переменной или возвращаемого значения
Катавасия
void питаться()
void спать()
Мышь найтиМышь()
...
GRASP: low coupling
Минимазация зацепления: полиморфизм
- Не использовать атрибуты другого типа (кроме стандартных)
- Не вызывать службы классов другого типа
- Не использовать другой тип для локальных переменных или возвращаемого значения
- Отказаться от наследования
- Удалить реализацию интерфейсов
Катавасия
void питаться()
void спать()
...
Нулевое зацепление... А как же ООП?
GRASP: low coupling
Высокая связность (сцепленность)
GRASP: high cohesion
Как обеспечить сфокусированность обязанностей объектов, их управляемость и ясность?
Сфокусируйтесь на обязанностях класса!
GRASP: high cohesion
Связный (сцепленный класс)
Менее связный (несцепленный класс)
приватное свойство или метод
публичное свойство или метод
GRASP: high cohesion / low coupling
Максимизируйте сфокусированность
Минимизируйте зависимости
приватное свойство или метод
публичное свойство или метод
GRASP: protected variations
Устойчивость к изменениям
Как спроектировать систему, чтобы изменение элементов не оказывало нежелательного влияния на другие элементы?
Распределите обязанности, чтобы обеспечить устойчивый интерфейс!
GRASP: protected variations
Точки вариации и точки эволюции
Точка эволюции - предполагаемая точка ветвления, которая может возникнуть в будущем
Точка вариации - точка ветвления в существующей на данный момент системе
GRASP: polymorphism
Полиморфизм
Как обрабатывать альтернативные варианты поведения на основе типа?
Для однотипных классов используйте полиморфные операции!
GRASP: шаблон-роль information expert
Информационный эксперт
Каков базовый принцип распределения обязанностей?
Назначьте обязанность тому классу, который обладает достаточной информацией для ее выполнения!
GRASP: шаблон-роль information expert
Эксперт: пример
GRASP: шаблон-роль creator
Создатель
Какой класс должен отвечать за создание нового экземпляра некоторого класса?
Смотри дальше!
GRASP: шаблон-роль creator
Создатель
- Содержит или агрегирует объекты целевого класса
- Активно использует
- Обладает данными инициализации
- Записывает экземпляры целевого класса
Например, при разработке набора настольных игр, объект класса Board включает в себя 0..n
объектов класса Square
GRASP: шаблон-роль controller
Контроллер
Какой класс должен отвечать координацию выполнения системных операций, поступающих
на уровне UI?
не переключайся!
GRASP: шаблон-роль controller
Контроллер - корневой объект
Контроллер - корневой объект
(Внешний контроллер)
Калькулятор пенсии
Контроллер
Веб-сервис
GRASP: шаблон-роль controller
Контроллер прецедента
LadyHandler
MilitaryHandler
ПрецедентHandler
Калькулятор пенсии
Веб-сервис
GRASP: шаблон-роль indirection
Перенаправление
Как распределить обязанности, чтобы снизить уровень зацепления несвязанных напрямую классов?
Присвойте обязанности промежуточному объекту, который перенаправляет связи!
GRASP: шаблон-роль indirection
Перенаправление
Адаптер расчета пенсии
ПрецедентHandler
Калькулятор пенсии
GRASP: шаблон-роль pure fabrication
Чистая выдумка (синтетика)
Какой класс должен обеспечить реализацию high cohesion/low coupling, если шаблон expert не подходит ?
Присвойте обязанности искусственному классу, не представляющему конкретного понятия предметной области!
Итоги GRASP
- Предпочтение отдается автономным модулям
- При усложнении класса определенная функциональность или связь с другими классами выносится в отдельную структуру
- Точки изменения защищаются полиморфными операциями
- Необходим баланс между вероятностью модификаций и усилиями для обеспечения робастного решения
Выводы
Цель программной инженерии - построения ПО высокого качества.
Качество ПО лучше всего видится как компромисс между целым рядом различных целей, а не как единый фактор
Действительное значение имеют внешние факторы, понятные пользователям (корректность, простота использования, эффективность, своевременность и др.)
Принципы SOLID и GRASP направлены на улучшение качества внутренних факторов, и только через внутренние факторы возможно управление системой.
(по мотивам книг Бертрана Майер)
Библиография
Крэг Ларман. Применение UML 2.0 и шаблонов проектирования (ozon.ru)
Гамма Эрих, Влиссидес Джон, Хелм Ричард, Джонсон Р. Приемы объектно-ориентированного проектирования. Паттерны проектирования (ozon.ru)
Кристофер Александер. Язык шаблонов. Города. Здания. Строительство (ozon.ru)
Блог С. Теплякова (http://sergeyteplyakov.blogspot.com)
SOLID
Р. Мартин "Принципы, паттерны и методики гибкой разработки на языке C#"
Р. Мартин "Чистая архитектура. Искусство разработки программного обеспечения"
Р. Мартин "Чистый код: создание, анализ и рефакторинг"
GRASP
Kent Beck, Ward Cunningham Using Pattern Languages for Object-Oriented Programs
Проектирование
By ateteryatnikov-sis
Проектирование
- 313