Принципы и шаблоны проектирования GRASP / SOLID
Тетерятников Алексей
Романенко Константин
1. Изучить:
Цель доклада
-
принципы SOLID
-
шаблоны GRASP
2. Поделиться с коллегами :)
Серия докладов
1) SOLID
2) GRASP
Подведение итогов
История появления принципов проектирования
S - (SRP) Принцип единственной ответственности
O - (OCP) Принцип открыт / закрыт
L - (LSP) Принцип подстановки Барбары Лисков
I - (ISP) Принцип разделения интерфейсов
D - (DIP) Принцип обращения зависимостей
SOLID
SOLID
Подведение итогов
История появления принципов проектирования
S - (SRP) Принцип единственной ответственности
O - (OCP) Принцип открыт / закрыт
L - (LSP) Принцип подстановки Барбары Лисков
I - (ISP) Принцип разделения интерфейсов
D - (DIP) Принцип обращения зависимостей
Зарождение принципов проектирования
Вспомним 70-е...
Кристофер Александер
Архитектурные шаблоны
Шаблоны программирования
Время | Веха | Имена |
---|---|---|
середина 1980-х | шаблоны программирования | Кент Бек, Уорд Каннингем |
1988-1994 | шаблоны "Банды четырех" (GoF) | Эрих Гамма Ричард Хелм Ральф Джонсон Джон Влиссидес [ozon.ru] |
Принципы проектирования
Время | Веха | Имена |
---|---|---|
1997 | принципы и шаблоны GRASP | Крэг Ларман [ozon.ru] |
2002 | принципы SOLID |
Роберт Мартин |
Крэг Ларман
Разработчик APL, Lisp, Smalltalk
принципы GRASP
соавтор большого масштабируемого Scrum (Large-Scale Scrum)
1970-е
1997
2005
Консультант и автор в области разработки ПО
Автор известных книг:
- Чистый код
- Чистая архитектура
- Принципы, паттерны и методики гибкой разработки на языке C#
Роберт Мартин
Дядя Боб (Uncle Bob)
Бертран Майер
- Автор принципа OCP
- Программирование по контракту
- Создатель языка программирования Эйфель.
- C 2015 года преподаватель в университете Иннополис (Иннополис, Республика Татарстан, Россия).
Автор известных книг:
- Почувствуй класс
- Объектно-ориентированное конструирование программных систем
Принципы Проектирования SOLID
А что такое
"Принципы Проектирования"?
Принципы проектирования по Б. Майеру
Принцип проектирования – это методологическое правило, которое выражает общий взгляд на разработку ПО.
Принцип проектирования – это методологическое правило, которое выражает общий взгляд на разработку ПО.
Принципы проектирования по Б. Майеру
Хороший принцип является одновременно:
Абстрактным -
принцип должен описывать универсальное правило, а не конкретную практику.
Опровергаемым -
у разумного человека должна быть возможность не согласиться с принципом.
SOLID как устранение недостатков ПО
- Жесткость
- Хрупкость
- Косность
- Вязкость
- Ненужная сложность
- Ненужные повторения
- Непрозрачность
Принцип единственной ответственности
SRP
LSP
ISP
DIP
OCP
Задаемся вопросом:
"что делает класс?"
Уход за котом
- Кормить
- Развлекать
SRP
LSP
ISP
DIP
OCP
1. Декомпозиция на ответственности
Описывается порядок ухода за котом -
ответственность одна?
2. Причина для изменений должна быть только одна
SRP
LSP
ISP
DIP
OCP
Уход за котом
- Кормить
- Развлекать
Наполнить миску
Приготовить
Проверить запасы
{
Светить лазером
Бросать мяч
}
{
}
Погладь
3. Изменяемые вместе элементы должны быть рядом
Кормить
Развлекать
Уход за котом
Светить лазером
Бросать мяч
Наполнить миску
Приготовить
Проверить запасы
SRP
LSP
ISP
DIP
OCP
Погладь
Что должно храниться в одном классе?
Кормить
Наполнить миску
Приготовить
Проверить запасы
SRP
LSP
ISP
DIP
OCP
Изменения одного элемента обязательно приведет к изменению других
Нет причин изменять элемент отдельно
Класс имеет высокую связанность
Итоги SRP
1. Код разбивается на мелкие модули;
2. Четкое понимание, что делает модуль;
3. При совместной разработке упрощает процесс слияния кода;
SRP
LSP
ISP
DIP
OCP
4. Убирает лишние зависимости.
SRP
LSP
ISP
DIP
OCP
Принцип подстановки Барбары Лисков
- Должна быть возможность вместо базового типа подставить любой его подтип;
SRP
LSP
ISP
DIP
OCP
Принцип подстановки Барбары Лисков
- Логика родителя не должна нарушаться.
SRP
LSP
ISP
DIP
OCP
Тигр - тоже кот
Требования к программе меняются...
SRP
LSP
ISP
DIP
OCP
Определяем тигра
Какой китикет?
Это ж Тигр!
switch(еда) {
case 'мясо':
case 'китикет':
сытость = 80%
Базовый Кот
сытость = 50%
}
Кормить(еда) {
}
SRP
LSP
ISP
DIP
OCP
Определяем тигра
switch(еда) {
case 'мясо':
case 'китикет':
сытость = 80%
Базовый Кот
сытость = 50%
}
Throw "Такое не ем!"
Кормить(еда) {
}
switch(еда) {
case 'мясо':
case 'китикет':
сытость = 80%
Тигр
}
Кормить(еда) {
}
Throw Exception!
Тигр - не кот
SRP
LSP
ISP
DIP
OCP
Спи в коробке 50x50 см
Бегай за лазером
Китикет кушай
Мясо не кушай
SRP
LSP
ISP
DIP
OCP
Ошибки в наследовании
Exception
Неожиданное поведение для клиентов
if (кот Сиамский)
...
elseif (кот Сфинкс)
...
Проверка типа в базовом классе
Ошибки в наследовании
Невозможно установить правильность модели, рассматриваемой изолированно
SRP
LSP
ISP
DIP
OCP
Правильность модели можно выразить только в терминах ее клиентов
- Программирование через тестирование
SRP
LSP
ISP
DIP
OCP
- Программирование по контракту
Как предусмотреть требования клиентов?
- Опыт (Экстрасенсорные способности )
Предусловия:
Постусловия:
Программирование по контракту
SRP
LSP
ISP
DIP
OCP
Кот:
Длина: <50 см;
Пушистый;
Вес: <20 кг.
Сытый Кот
(сытость > 50%)
Кот
Кормить()
Правило для производных классов:
SRP
LSP
ISP
DIP
OCP
Программирование по контракту
- Не усиливать Предусловия
- Не ослаблять Постусловия
Итоги LSP
1. Согласует поведение базового и производного класса;
2. Гарантирует корректность работы ПО при замене базового класса производным.
SRP
LSP
ISP
DIP
OCP
SRP
LSP
ISP
DIP
OCP
Принцип разделения интерфейсов
Клиенты не должны вынужденно зависеть от методов, которыми не пользуются
SRP
LSP
ISP
DIP
OCP
Принцип разделения интерфейсов
Интерфейс кота
Имя
Жирные интерфесы
Уход за котом
SRP
LSP
ISP
DIP
OCP
Выставка котов
Уход за котом
+
Интерфейс кота
Титул
Имя
Назначить титул (v)
SRP
LSP
ISP
DIP
OCP
Жирные интерфесы
Выставка котов
Ветеринарная клиника
Уход за котом
Интерфейс кота
История болезней
Титул
Имя
Назначить титул (v)
Внести болезнь (v)
+
+
SRP
LSP
ISP
DIP
OCP
Жирные интерфесы
Выставка котов
Ветеринарная клиника
Уход за котом
Интерфейс кота
Имя
Интерфейс болеющего
Имя
История болезней
Внести болезнь (v)
Интерфейс Конкурсанта
Имя
Титул
Назначить титул (v)
SRP
LSP
ISP
DIP
OCP
Жирные интерфесы
Что делать если в интерфейсе есть itemID:int,
а вам он не нужен ?
Пример из Flex интерфейса Эталона
SRP
LSP
ISP
DIP
OCP
Создаем переменную "uselessVarForInterfaceImplementation"
SRP
LSP
ISP
DIP
OCP
Пример из Flex интерфейса Эталона
Итоги ISP
1. Код разбивается на мелкие модули;
2. уменьшает связанность между классом и его клиентом (low coupling).
SRP
LSP
ISP
DIP
OCP
Принцип инверсии зависимости
SRP
LSP
ISP
DIP
OCP
-
Модули верхнего уровня не должны зависеть от модулей нижнего уровня.
И те и другие должны зависеть от абстракций.
SRP
LSP
ISP
DIP
OCP
Принцип инверсии зависимости
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Зависимости исходного кода следуют за потоком управления
SRP
LSP
ISP
DIP
OCP
Инверсия зависимости
SRP
LSP
ISP
DIP
OCP
- Не должно быть переменных, в которых хранятся ссылки на конкретные классы.
- Не должно быть классов, производных от конкретных классов.
-
Не должно быть методов, переопределяющих метод, реализованный в одном из базовых классов
Модули должны зависеть от абстракции
SRP
LSP
ISP
DIP
OCP
Коробка
Постели подушку на дно
Подклей коробку (он её сгрыз)
Жди пока кот выспится
Кот
Спальня
Спать()
SRP
LSP
ISP
DIP
OCP
Пример: Спальня кота
Коробка
Шкаф
Постели подушку на дно
Подклей коробку (он её сгрыз)
Жди пока кот выспится
Подними кота на шкаф
Подклей угол шкафа (он его сгрыз)
Жди пока кот выспится
Кот
Спальня
Спать()
SRP
LSP
ISP
DIP
OCP
Пример: Спальня кота
Абстрактное спальное место
Шкаф
Домик для кота
Коробка
Подготовка
Восстановление
Жди пока кот выспится
Кот
Спальня
Спать()
SRP
LSP
ISP
DIP
OCP
Пример: Спальня кота
Итоги DIP
1. Контроль над зависимостями в исходном коде;
2. Позволяет сделать модули автономными, переиспользуемыми;
SRP
LSP
ISP
DIP
OCP
Принцип
открытости/закрытости
SRP
LSP
ISP
DIP
OCP
Программные сущности должны быть:
- открыты для расширения
- закрыты для модификации
SRP
LSP
ISP
DIP
OCP
Принцип
открытости/закрытости
SRP
LSP
ISP
DIP
OCP
Формулировка по Бертрану Майеру
-
Модуль называют открытым, если он еще доступен для расширения.
(имеется возможность расширить множество операций в нем или добавить поля к его структурам данных)
-
Модуль называют закрытым, если он доступен для использования другими модулями.
(Интерфейс модуля уже имеет строго определенное окончательное описание)
Разделять модули, которые изменяются по разным причинам
Как достигается?
DIP
+
Выстраивать зависимости таким образом, чтобы не было зависимостей от наиболее изменяемых модулей
SRP
SRP
LSP
ISP
DIP
OCP
Абстрактное спальное место
Шкаф
Домик для кота
Коробка
Подготовка
Восстановление
Жди пока кот выспится
Кот
Спальня
Спать()
SRP
LSP
ISP
DIP
OCP
Пример: Спальня кота
Примеры OCP в Эталоне
- QueryObject
- SOU.ETA.View.Reorganize.Reorganizer
- SOU.System.Configuration
Новое поведение импорта / экспорта задается добавлением нового наследника ConfigItem
Реорганизация представлений
SRP
LSP
ISP
DIP
OCP
Механизм построения SQL запросов
- .
SRP
LSP
ISP
DIP
OCP
Примеры OCP в СИС
Итоги SOLID
Принципы нацелены на создание структур, которые:
- Легко изменяются по требованиям
- Просты и понятны
- Повторно используемые
Спасибо за внимание!
- Блог С. Теплякова (http://sergeyteplyakov.blogspot.com)
- Р. Мартин "Принципы, паттерны и методики гибкой разработки на языке C#"
- Р. Мартин "Чистая архитектура. Искусство разработки программного обеспечения"
- Р. Мартин "Чистый код: создание, анализ и рефакторинг"
Библиография
SOLID
By ateteryatnikov-sis
SOLID
- 237