Принципы и шаблоны проектирования GRASP / SOLID

Тетерятников Алексей

Романенко Константин

1. Изучить:

Цель доклада

  • принципы SOLID  

  • шаблоны GRASP

2. Поделиться с коллегами :)

Серия докладов

1) SOLID

2) GRASP

Подведение итогов

История появления принципов проектирования

S  - (SRP) Принцип единственной ответственности

O - (OCP) Принцип открыт / закрыт

L  - (LSP) Принцип подстановки Барбары Лисков

I   - (ISP) Принцип разделения интерфейсов

D - (DIP) Принцип обращения зависимостей

SOLID

SOLID

Подведение итогов

История появления принципов проектирования

- (SRP) Принцип единственной ответственности

O - (OCP) Принцип открыт / закрыт

- (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)

Бертран Майер

 

Автор известных книг:

  • Почувствуй класс
  • Объектно-ориентированное конструирование программных систем

Принципы Проектирования 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

Принципы нацелены на создание структур, которые:

  • Легко изменяются по требованиям
  • Просты и понятны
  • Повторно используемые

Спасибо за внимание!

  • Р. Мартин "Принципы, паттерны и методики гибкой разработки на языке C#"
  • Р. Мартин "Чистая архитектура. Искусство разработки программного обеспечения"
  • Р. Мартин "Чистый код: создание, анализ и рефакторинг"

Библиография

SOLID

By Сибирские интеграционные системы