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

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

 

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

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

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

Минимазация зацепления: атрибуты

  1. Избавиться от атрибутов другого типа (кроме стандартных)

Катавасия

датаРождения: DateTime

кличка: String

ночноеЗрение: NightVision

GRASP: low coupling

Минимизация зацепления: методы

  1. Не использовать атрибуты другого типа (кроме стандартных)
  2. Не вызывать службы классов другого типа
  3. Не использовать другой тип для параметров, локальной переменной или возвращаемого значения

Катавасия

void питаться()

void спать()

Мышь найтиМышь()

...

GRASP: low coupling

Минимазация зацепления: полиморфизм

  1. Не использовать атрибуты другого типа (кроме стандартных)
  2. Не вызывать службы классов другого типа
  3. Не использовать другой тип для локальных переменных или возвращаемого значения
  4. Отказаться от наследования
  5. Удалить реализацию интерфейсов

Катавасия

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

Создатель

  1. Содержит или агрегирует объекты целевого класса
  2. Активно использует
  3. Обладает данными инициализации
  4. Записывает экземпляры целевого класса

Например, при разработке набора настольных игр, объект класса 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

Made with Slides.com