Post on 22-Apr-2015
description
SOLID – принципы объектно-ориентированного дизайна
О чем мы сегодня поговорим
Что такое «плохой» дизайн кода в чем он выражается какие несет проблемы
Принципы проектирования SOLID Назначение Примеры использования
Как обнаруживать и устранять плохой дизайн кода Запахи кода Системы статического анализа кода
Что такое «плохой» дизайн кода?
Что такое «плохой» дизайн кода?
Повторение Отсутствие возможности
повторного использования кода Монолитность Плохая читаемость кода Неоправданная сложность Вязкость Хрупкость
Откуда берется «плохой» дизайн?
Откуда берется «плохой» дизайн?
Внесение изменений в систему, которая проектировалась без учета возможности появления таковых
Изменения в системе могут потребоваться на любой стадии проекта: Аналитика Разработка Сопровождение
Изменения в системе неизбежны
Отсутствие формализированных требований со стороны клиента
Уточнение требований на этапе разработки, после утверждения ТЗ
Реализация дополнительного функционала на этапе технического сопровождения системы
Изменения в системе неизбежны
Мы должны помнить об этом
Строить процесс разработки итеративно, с поправкой на то, чтобы внесение последующих изменений стоило нам как можно меньше ресурсов
Перечислим основные проблемы «плохого» дизайна
Дубликаты структур, которые должны иметь
общую абстракцию.
Повторение
Сложно выделить компоненты, которые можно использовать
повторно
Отсутствие возможности повторного использования
Монолитность
Систему сложно изменять
Код сложно понимать
Неоправданная сложность
В системе есть инфраструктура, которая или не используется, или используется неправильно
Вязкость
Делать что-то правильно сложнее, чем делать это неправильно
Изменения легко ломают систему и приводят к
новым изменениям
Хрупкость
Может получится как в комиксе – «Читая чужой код»
Решение есть
Single Responsibility
Open/Closed
Liskov Substitution
Interface Segregation
Dependency Inversion
Что это такое?
SOLID - это аббревиатура пяти основных принципов дизайна классов в объектно-ориентированном проектировании
Когда появились?
Были сформулированы Робертом Мартином в далеком 1995 году
Single Responsibility
Single Responsibility
У класса есть только одна ответственность, он умеет ее делать и делает ее хорошо.
Не должно быть больше одной причины для изменения класса.
Single Responsibility
Пример приложения «Прямоугольник»:
Требования:Расчет площади прямоугольникаВывод изображения прямоугольника на UI
Single Responsibility
Single Responsibility
Single Responsibility
Пример приложения «Модем»:
Требования:Установка соединения по телефонному номеруЗавершение соединенияОтправка данныхПрием данных
Single Responsibility
Single Responsibility
Single Responsibility
У класса есть только одна ответственность, он умеет ее делать и делает ее хорошо.
Не должно быть больше одной причины для изменения класса.
Open/Closed
Open/Closed
Программные сущности (классы, модули, методы и т.д.) должны
быть открыты для расширения, но закрыты от изменений
Open/Closed
Как этого добиться?
Open/Closed
Классы должны зависеть от абстракций
Новые фичи могут быть добавлены путем реализации абстракций
Open/Closed
Использование паттернов Стратегия Шаблонный метод
Open/Closed
Приложение «Почтовый клиент»
Требования:
Отправка почтыЗапись результатов работы в файл
Open/Closed
Open/Closed
Open/Closed
Приложение «Почтовый клиент»
Новое требование:
Запись результатов работы на диск
Open/Closed
Open/Closed
Open/Closed
Паттерн Стратегия
Open/Closed
Open/Closed
Open/Closed
Open/Closed
Паттерн Шаблонный метод
Open/Closed
Задача:
Разработать класс, реализующий шифрование данных при помощи алгоритмов DES и RSA
Open/Closed
Open/Closed
Open/Closed
Open/Closed
Программные сущности (классы, модули, методы и т.д.) должны
быть открыты для расширения, но закрыты от изменений
Liskov Substitution
Liskov Substitution
Пусть q(x) является свойством, верным относительно
объектов x некоторого типа T.
Тогда q(y) также должно быть верным для объектов y типа S,
где S является подтипом типа T.
Liskov Substitution
Клиенты, использующие базовый класс, должны работать и с его наследниками, не зная этого
Liskov Substitution
ЗадачаМы хотим реализовать свой список с интерфейсом IList<T>. Его особенностью будет то, что все записи в нем дублируются.
Данная реализация не представляет никакой опасности, если рассматривать ее изолированно.
Liskov SubstitutionВзглянем на использование этого класса с точки зрения клиента.
Клиент, абстрагируясь от реализаций, пытается работать со всеми объектами типа IList одинаково:
Liskov Substitution
Поведение списка DoubleList отличается от типичных реализаций IList. Получается, что наш DoubleList не может быть заменен базовым типом. Это и есть нарушение принципа замещения Лисков.
Проблема заключается в том, что теперь клиенту необходимо знать о конкретном типе объекта, реализующем интерфейс IList. В качестве такого объекта могут передать и DoubleList, а для него придется выполнять дополнительную логику и проверки.
Liskov Substitution
РешениеПравильным решением будет использовать свой собственный интерфейс, например, IDoubleList.
Этот интерфейс будет объявлять для пользователей поведение, при котором добавляемые элементы удваиваются.
Liskov Substitution
Проектирование по контракту
Liskov Substitution
Предусловия не могут быть ужесточены в наследнике
Постусловия не могут быть ослаблены в наследнике
Инварианты базового типа должны соблюдаться и в наследнике
Liskov Substitution
Рассмотрим пред и постусловия для интерфейса IList. Для функции Add:•предусловие: item != null•постусловие: count = oldCount + 1
Для нашего DoubleList и его функции Add:•предусловие: item != null•постусловие: count = oldCount + 2
Liskov Substitution
Interface Segregation
Interface Segregation
Клиентам не должны навязываться интерфейсы, которые им не нужны
Interface Segregation
Interface Segregation
Interface Segregation
Много небольших интерфейсов лучше чем один большой
Dependency Inversion
Dependency Inversion
Зависимости внутри системы строятся на основе абстракций.
Модули верхнего уровня не зависят от модулей нижнего уровня.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Dependency Inversion
Приложение «Печатная машинка»
Приложение должно уметь: Читать текст с клавиатуры Выводить текст на печать на
принтер
Dependency Inversion
Dependency Inversion
Dependency Inversion
Приложение «Печатная машинка»
Появилось новое требование Приложение должно уметь
сохранять текст на диск
Dependency Inversion
Dependency Inversion
Пример применения принципа DI
Dependency Inversion
Dependency Inversion
Dependency Inversion
Отсутствие инверсии зависимостей не всегда плохо.
Пример, зависимость от класса String.
Dependency Inversion
Зависимости внутри системы строятся на основе абстракций.
Модули верхнего уровня не зависят от модулей нижнего уровня.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Dependency Inversion
Поддержание кода в хорошей форме
Поддержание кода в хорошей форме
Проводить ревью кода
Находить «запахи» кода
Использовать статические анализаторы кода
Code smells
Static program analysis
Code Static analyze system
Code Static analyze system
YAGNI & KISS
You Aren’t Gonne Need It
Keep It Simple Stupid
О чем мы сегодня поговорили
Что такое плохой дизайн и откуда он берется
Как использовать принципы SOLID для улучшения дизайна
Как поддерживать код в хорошем состоянии
Использованные материалы
Материалы xpinjection.com - http://xpinjection.com/resources/ Блог Александра Бындю - http://blog.byndyu.ru/ Книга «Принципы, паттерны и методики гибкой разработки
на языке C#» - http://www.ozon.ru/context/detail/id/5800704/ Книга «Инфраструктура программных проектов. Соглашения,
идиомы и шаблоны для многократно используемых библиотек .NET» - http://www.ozon.ru/context/detail/id/5588868/
Контакты
Трёшников Павел Ведущий разработчик СМС-ИТ
▪ www.sms-automation.ru
e-mail: treshnikov@gmail.com twitter: @treshnikov