SlideShare a Scribd company logo
1 of 42
SOLIDарность
Тестирование, как
разработка
Об авторе
Игорь Горчаков
skype : igor-gorchakov
e-mail : gorchakov.igiv@gmail.com
Качество кода в тестировании
• Многие не считают, что разработка
автотестов – это разработка.
• Иногда и разработчик автотестов не
считает, что он занимается разработкой
Что такое хороший код?
• Что такое код?
• Что такое хороший код?
• Почему важен хороший код?
Дизайн
Дизайн – это результат
процесса
проектирования :
декомпозиция системы,
определение поведения
и характеристики
отдельных компонент и
их взаимодействия.
Отсутствие гибкости (Rigidity)
Код тяжело поддается изменениям.
Изменения в одном месте, вызывают
изменения в других частях системы
приводя к эффекту «снежного кома».
Хрупкость (Fragility)
Код легко «ломается». Т.е. после
осуществления изменения код может
сломаться еще в нескольких других
местах.
Монолитность (Immobility)
Компоненты
настолько сильно
связаны друг с
другом, что их
сложно разделить
друг от друга и
переиспользовать.
Вязкость (Viscosity)
Сделать что-то хорошо, очень сложно.
Существующий код сам провоцирует на
дальнейшее увеличение количества
«хаков» и «костылей».
Излишняя сложность
(Needless complexity)
Код содержит в себе архитектурные
решения, необходимость в которых
еще не назрела.
Излишняя повторяемость
(Needless repetition)
код содержит в
себе
дублирование,
особенно такое,
которое легко
устраняется
Нечитабельность (Opacity)
код сложен
для чтения и
понимания.
Признаки хорошего дизайна
• Гибкость
• Прочность
• Переиспользуемость
• Отсутствие вязкости
• Простота
• Отсутствие
дублирования кода
• Читабельность
Что такое SOLID?
• S - Single responsibility principle
• O - Open/closed principle
• L - Liskov substitution principle
• I - Interface segregation principle
• D - Dependency inversion principle
Single responsibility principle
Не должно быть больше одной причины
для изменения класса
Примеры
Примеры
Решение
Open/closed principle
программные сущности (классы,
модули, методы и т.д.) должны быть
открыты для расширения, но закрыты
для изменения
Пример
Пример
Решение
Liskov substitution principle
Объекты в программе могут быть
заменены их наследниками без
изменения свойств программы
Пример
Пример
Решение
Решение
Interface segregation principle
Много
специализирован
ных интерфейсов
лучше, чем один
универсальный.
Пример
Пример
Решение
Решение
Dependency inversion principle
Модули верхнего уровня не должны
зависеть от модулей нижнего уровня.
Оба должны зависеть от абстракции.
Абстракции не
должны зависеть от
деталей.
Детали должны
зависеть от
абстракций.
Пример
Пример
Тестируемость?
Решение
Решение
Тестируемость
Не забываем!
• DRY – Don’t repeat yourself (не повторяй
себя)
• KISS – keep it simple stupid (делайте
вещи проще)
• YAGNI - You ain’t gonna need it – вам это
не понадобится
Заключение
Принципы, а не строгие
правила
Нет универсальных
рецептов
Вопросы?
Об авторе :
Игорь Горчаков
skype : igor-gorchakov
e-mail :
gorchakov.igiv@gmail.com
Литература : Принципы, паттерны и методики гибкой разработки на языке C#
(Agile Principles, Patterns and Practices in C#)

More Related Content

Similar to SOLIDарность: Тестирование как разработка

CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...
CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...
CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...
CodeFest
 
Работа с унаследованным кодом. Есть ли жизнь после коммита.
Работа с унаследованным кодом. Есть ли жизнь после коммита.Работа с унаследованным кодом. Есть ли жизнь после коммита.
Работа с унаследованным кодом. Есть ли жизнь после коммита.
Vadim Kruchkov
 
S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014
S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014
S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014
Python Meetup
 
SOLID & GRASP
SOLID & GRASPSOLID & GRASP
SOLID & GRASP
devel123
 
Test Driven Development in .NET Applications
Test Driven Development in .NET ApplicationsTest Driven Development in .NET Applications
Test Driven Development in .NET Applications
Anton Vidishchev
 
Benefits of unit-testing and inversion of controll
Benefits of unit-testing and inversion of controllBenefits of unit-testing and inversion of controll
Benefits of unit-testing and inversion of controll
Mykyta Hopkalo
 

Similar to SOLIDарность: Тестирование как разработка (20)

Solid code via tdd
Solid code via tddSolid code via tdd
Solid code via tdd
 
Большие проекты, архитектура и фреймворки.
Большие проекты, архитектура и фреймворки.Большие проекты, архитектура и фреймворки.
Большие проекты, архитектура и фреймворки.
 
Проблемы точечной застройки в больших городах или зачем нужен Dagger
Проблемы точечной застройки в больших городах или зачем нужен DaggerПроблемы точечной застройки в больших городах или зачем нужен Dagger
Проблемы точечной застройки в больших городах или зачем нужен Dagger
 
Как писать красивый код или основы SOLID
Как писать красивый код или основы SOLIDКак писать красивый код или основы SOLID
Как писать красивый код или основы SOLID
 
Принципы SOLID
Принципы SOLIDПринципы SOLID
Принципы SOLID
 
Design principles
Design principles Design principles
Design principles
 
XP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeXP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy code
 
SOLID
SOLIDSOLID
SOLID
 
CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...
CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...
CodeFest 2014. Шкредов С. — Управление зависимостями в архитектуре. Переход о...
 
Работа с унаследованным кодом. Есть ли жизнь после коммита.
Работа с унаследованным кодом. Есть ли жизнь после коммита.Работа с унаследованным кодом. Есть ли жизнь после коммита.
Работа с унаследованным кодом. Есть ли жизнь после коммита.
 
JS Lab2017_Алексей Зеленюк_Сбалансированное окружение для вашей продуктивности
JS Lab2017_Алексей Зеленюк_Сбалансированное окружение для вашей продуктивностиJS Lab2017_Алексей Зеленюк_Сбалансированное окружение для вашей продуктивности
JS Lab2017_Алексей Зеленюк_Сбалансированное окружение для вашей продуктивности
 
S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014
S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014
S.O.L.I.D. - Павел Кохан, Python Meetup 26.09.2014
 
SOLID & GRASP
SOLID & GRASPSOLID & GRASP
SOLID & GRASP
 
SOLID && Magento2
SOLID && Magento2SOLID && Magento2
SOLID && Magento2
 
SOLID
SOLIDSOLID
SOLID
 
Test Driven Development in .NET Applications
Test Driven Development in .NET ApplicationsTest Driven Development in .NET Applications
Test Driven Development in .NET Applications
 
Компонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективноКомпонентный подход: скучно, неинтересно, бесперспективно
Компонентный подход: скучно, неинтересно, бесперспективно
 
Rambler.iOS #5: Подмодули в VIPER
Rambler.iOS #5: Подмодули в VIPERRambler.iOS #5: Подмодули в VIPER
Rambler.iOS #5: Подмодули в VIPER
 
SOLID Principles in the real world
SOLID Principles in the real worldSOLID Principles in the real world
SOLID Principles in the real world
 
Benefits of unit-testing and inversion of controll
Benefits of unit-testing and inversion of controllBenefits of unit-testing and inversion of controll
Benefits of unit-testing and inversion of controll
 

More from SQALab

More from SQALab (20)

Готовим стажировку
Готовим стажировкуГотовим стажировку
Готовим стажировку
 
Куда приводят мечты? или Искусство развития тестировщика
Куда приводят мечты? или Искусство развития тестировщикаКуда приводят мечты? или Искусство развития тестировщика
Куда приводят мечты? или Искусство развития тестировщика
 
Оптимизация Selenium тестов и ускорение их поддержки
Оптимизация Selenium тестов и ускорение их поддержкиОптимизация Selenium тестов и ускорение их поддержки
Оптимизация Selenium тестов и ускорение их поддержки
 
Автоматизация 0.0: 0 - бюджет, 0 - опыт программирования
Автоматизация 0.0: 0 - бюджет, 0 - опыт программированияАвтоматизация 0.0: 0 - бюджет, 0 - опыт программирования
Автоматизация 0.0: 0 - бюджет, 0 - опыт программирования
 
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
Нагрузочное тестирование нестандартных протоколов с использованием Citrix и J...
 
Continuous performance testing
Continuous performance testingContinuous performance testing
Continuous performance testing
 
Конфиги вместо костылей. Pytestconfig и зачем он нужен
Конфиги вместо костылей. Pytestconfig и зачем он нуженКонфиги вместо костылей. Pytestconfig и зачем он нужен
Конфиги вместо костылей. Pytestconfig и зачем он нужен
 
Команда чемпионов в ИТ стихии
Команда чемпионов в ИТ стихииКоманда чемпионов в ИТ стихии
Команда чемпионов в ИТ стихии
 
API. Серебряная пуля в магазине советов
API. Серебряная пуля в магазине советовAPI. Серебряная пуля в магазине советов
API. Серебряная пуля в магазине советов
 
Добиваемся эффективности каждого из 9000+ UI-тестов
Добиваемся эффективности каждого из 9000+ UI-тестовДобиваемся эффективности каждого из 9000+ UI-тестов
Добиваемся эффективности каждого из 9000+ UI-тестов
 
Делаем автоматизацию проектных KPIs
Делаем автоматизацию проектных KPIsДелаем автоматизацию проектных KPIs
Делаем автоматизацию проектных KPIs
 
Вредные привычки в тест-менеджменте
Вредные привычки в тест-менеджментеВредные привычки в тест-менеджменте
Вредные привычки в тест-менеджменте
 
Мощь переполняет с JDI 2.0 - новая эра UI автоматизации
Мощь переполняет с JDI 2.0 - новая эра UI автоматизацииМощь переполняет с JDI 2.0 - новая эра UI автоматизации
Мощь переполняет с JDI 2.0 - новая эра UI автоматизации
 
Как hh.ru дошли до 500 релизов в квартал без потери в качестве
Как hh.ru дошли до 500 релизов в квартал без потери в качествеКак hh.ru дошли до 500 релизов в квартал без потери в качестве
Как hh.ru дошли до 500 релизов в квартал без потери в качестве
 
Стили лидерства и тестирование
Стили лидерства и тестированиеСтили лидерства и тестирование
Стили лидерства и тестирование
 
"Давайте не будем про качество"
"Давайте не будем про качество""Давайте не будем про качество"
"Давайте не будем про качество"
 
Apache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектовApache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектов
 
Тестирование геолокационных систем
Тестирование геолокационных системТестирование геолокационных систем
Тестирование геолокационных систем
 
Лидер или босс? Вот в чем вопрос
Лидер или босс? Вот в чем вопросЛидер или босс? Вот в чем вопрос
Лидер или босс? Вот в чем вопрос
 
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
От Зефира в коробке к Structure Zephyr или как тест-менеджеру перекроить внут...
 

SOLIDарность: Тестирование как разработка

Editor's Notes

  1. Поставить проблему и упомянуть о том, что доклад полезен будет тем, кто пишет на ОО ЯП.
  2. Первая проблема : уменьшение сроков на разработку. Отсутствия понимания, что необходимо время на юнит тестирование, ревью кода, рефакторинг. Как вариант – неверное соотношение разработчиков и «тест-разработчиков». Можно проводить беседы с руководителем, хорошо если компанию вам составят пара разработчиков самого объекта для тестирования. Можно объяснять почему это важно, приводить графики зависимости времени поддержки тест фреймворка, от длительности проекта и прочие атрибуты защиты разработки, как таковой. Вторая проблема : не стараемся писать хороший код, который еще долго придется поддерживать. Не пишем юнит тесты и спустя рукава подходим к тестированию реализованных решений. Не занимаемся ревью кода, рефакторингом и прочими «гигиеническими» процедурами. Нужно бороться за качество своего кода самостоятельно. Более того, как уверяют авторы трудов (к примеру, тот же Роб Мартин) – это всегда можно сделать. На последнем месте работы, несмотря на высокий темп разработки, у меня получалось выкраивать время (иногда по партизански завышая время на implementation ), объяснять руководителю о необходимости изменений в определенных местах и получении время и на рефакторинг и на код ревью. Но, главное в этом процессе – желание улучшать свой код. Как писал все тот же Роб Мартин, разработка – это ремесло 21-го века. И если вы хотите, чтобы подходить к «станку» было приятно, чтобы вы всегда могли найти нужный вам инструмент, не разбирая полчаса бардак и мусор - за своим рабочим местом нужно следить. В этом, на мой взгляд, кроется и удовольствие от программирования – в чистый и приятный код всегда легко и с удовольствием погружаешься. И испытываешь сильное отвращения погружаясь в запутанный, сложный «грязный» код. Это одна из причин, почему все так любят начинать новые проекты, пока код еще не захламлен «грязными» «быстрыми» изменениями уже в процессе его использования.
  3. Что такое код? Код - это то, что предоставлять клиенту сервис. Проходят ли тесты, низка ли цикломатическая сложность – это важно, но главное – клиент должен получить услугу. Таким образом, что такое хороший код? Это код который работает. Когда код просто для понимания мы легко можем его изменить, дополнить и реже ошибиться при изменении. Т.е. хороший код просто для изменений. Когда код прост для изменения – мы можем быстрее среагировать на feedback клиента, исправить bugs и улучшить сервис. Более того, когда код просто для понимания – мы и сами счастливее и получаем удовольствие от работы. Именно поэтому хороший код важен. Как же писать хороший код?
  4. Дизайн – это результат процесса проектирования : декомпозиция системы, определение поведения и характеристики отдельных компонент и их взаимодействия. Т.е. простыми словами, бизнес модель мы разбиваем на отдельные компоненты и определяем взаимодействие между ними. Как сделать хороший дизайн, который и приведет нас к хорошему коду? Давайте определимся, что точно мы бы хотели избежать в результате проектирования, а именно признаки плохого дизайна.
  5. Отсутствие гибкости – код тяжело поддается изменениям. Изменения в одном месте, вызывают изменения в других частях системы приводя к эффекту «снежного кома». Отсутствие гибкости проявляется в том случае, когда программа с трудом поддается даже простым изменениям. Дизайн перестает быть гибким, если одно изменение влечет за собой каскад последующих изменений в зависимых модулях. Чем больше модулей подвержено изменениям, тем менее гибким считается дизайн проекта. Большинство разработчиков, так или иначе, сталкиваются с этой проблемой. Их просят сделать простые на первый взгляд изменения. Они внимательно изучают характер будущих изменений, а затем выполняют обоснованную оценку требуемого в этом случае объема работы. Позднее, в процессе реализации изменений, разработчики сталкиваются с непредвиденными последствиями этих изменений. В частности, подвергаются переработке огромные блоки кода, причем в процесс модификации вовлекается намного больше модулей, чем планировалось в результате первоначальной оценки. В конце концов, внедрение изменений занимает намного больше времени, чем планировалось изначально. Если спросить разработчиков о том, почему не оправдались их расчеты, они будут жаловаться на то, что задача оказалась намного сложнее, чем предполагалось изначально.
  6. Хрупкость – Код легко «ломается». Т.е. после осуществления изменения код может сломаться еще в нескольких других местах. Зачастую новые проблемы возникают в тех областях, которые казалось бы не связаны с изменяемым компонентом. В процессе исправления этих ошибок возникают новые ошибки, в результате чего команда разработчиков начинает походить на собаку, гоняющуюся за собственным хвостом. По мере возрастания хрупкости программного модуля вероятность появления непредвиденных проблем приближается к 100%. Несмотря на всю кажущуюся абсурдность подобного утверждения, подобные модули встречаются нередко. Задачи переделки таких модулей могут бесконечно висеть в баг трекере, но никто из разработчиков не хочет за них браться.
  7. Монолитность – компоненты настолько сильно связаны друг с другом, что их сложно разделить друг от друга и переиспользовать. Дизайн проекта считается монолитным, если он содержит компоненты, которые могли бы применяться в других системах, однако усилия и степень риска, связанные с выделением этих компонентов из первоначальной системы, слишком велики. К сожалению, такое встречается весьма часто.
  8. Вязкость – сделать что-то хорошо, очень сложно. Существующий код сам провоцирует на дальнейшее увеличение количества «хаков» и «костылей». Вязкость может проявляться в двух формах: по отношению к ПО и к среде.В случае необходимости внесения изменений разработчики, как правило, видят несколько вариантов решения задачи. Некоторые из них сохраняют исходный дизайн проекта, а другие — нет (т.е. относятся к разряду хакерских приемов). Если предлагаемые дизайном методы сложнее в применении, чем хакерские приемы, то говорят, что вязкость проекта высока. В этом случае легко допустить ошибку, а правильные действия выполнить не так уж и просто. В идеале дизайн проекта должен быть таким, чтобы внесение изменений, не ухудшающих этот дизайну, осуществлялось легко и понятно. Симптом вязкости среды наблюдается в случае, если среда разработки характеризуется словами “медленный” и “неэффективный”. Например, если компиляция занимает очень долгое время, у разработчика может возникнуть желание изменять программный код таким образом, чтобы избежать полной рекомпиляции (даже если изменение ухудшает дизайн проекта). Если системе управления исходным кодом требуется несколько часов, чтобы обновить в репозитории всего несколько файлов, то разработчики могут захотеть сделать изменения, требующие как можно меньше обновлений в репозитории, даже если это приведет к ухудшению дизайна проекта. В обоих случаях вязкий проект представляет собой проект, в котором трудно сохранить качественный дизайн. Мы же хотим создавать такие системы, в которых можно без особых усилий сохранять качественный дизайн проекта и улучшать его.
  9. Излишняя сложность (Overdesign) – код содержит в себе архитектурные решения, необходимость в которых еще не назрела. Проект имеет неоправданную сложность, если содержит элементы, не использующиеся в настоящий момент времени. Это часто происходит в том случае, когда разработчики предсказывают изменения в требованиях и проводят мероприятия, направленные на то, чтобы справиться с этими потенциальными изменениями в будущем. На первый взгляд кажется, что это неплохо. В конце концов, подготовка к предстоящим изменениям должна сохранить наш код гибким и предотвратить кошмарные изменения, которые могут возникнуть впоследствии. К сожалению, эффект от таких мероприятий может быть совсем противоположным. При подготовке к большому количеству возможных изменений в требованях и непредвиденных ситуаций, проект начинает “замусориваться” частями кода, которые не используются, и возможно никогда не понадобятся. На практике некоторые из таких подготовительных мероприятий оправдываются и окупаются, но большинство — нет. Между тем проект несет на себе груз неиспользуемых элементов, а программа получается сложной и трудно понимаемой.
  10. Излишняя повторяемость – код содержит в себе дублирование, особенно такого, которое легко устраняется. Операции “вырезки” и “вставки” могут быть полезными при редактировании текста, но в то же время они могут быть опасными операциями в случае редактирования кода. Очень часто программные системы выстраиваются на десятках или сотнях повторяющихся элементов кода. Это происходит примерно следующим образом:Предположим, что Ральфу необходимо написать код, выполняющий некие функции. Он просматривает другие части кода, где, по его мнению, такие функции уже выполнялись и обнаруживает подходящий фрагмент. Он копирует и вставляет этот код в свой модуль и производит необходимые изменения.Ральфу неизвестно, что этот фрагмент кода был изначально позаимствован Тодом из модуля, написанного Лили. Причем Лили было нужно, чтобы ее код выполнял поиск натуральных чисел. Она быстро обнаружила, что код, выполняющий поиск целых чисел, тоже может применяться в этой ситуации. Затем она просто вырезала последний фрагмент кода и включила его в свой модуль, изменив соответствующим образом последний. Если один и тот же код появляется несколько раз в несколько различных формах, разработчики теряют абстракцию. Поиск всех повторений, а также их устранение с применением соответствующих абстракций может и не включаться в первые пункты списка приоритетов, но следует учитывать то, что в противном случае затрачивается немало усилий и времени для того, чтобы система получалась легкой для понимания и изменения в дальнейшем. Когда в проекте есть дублирование кода, это может сильно усложнить работу по изменению системы. Ошибки, обнаруженные в одном из блоков повторяющегося кода должны быть исправлены во всех местах, содержащих этот код. Тем не менее, поскольку каждое повторение может в незначительной степени отличаться от всех остальных, то исправления не всегда могут быть одинаковыми, и в такой ситуации легко допустить ошибку.
  11. Нечитабельность – код сложен для чтения и понимания. Неясность программного модуля проявляется в сложности его понимания. Код может быть написан либо в четкой и выразительной манере, либо быть непонятным и запутанным. Код, эволюционирующий с течением времени, обычно становится все более неясным. Необходимо постоянно следить за тем, чтобы код оставался “прозрачным” и выразительным, сводя возможную неясность к минимуму. В начале написания нового модуля его код может казаться разработчикам достаточно “прозрачным” и понятным, т.к. они, погружаяются в проблему с головой, и код может казаться проще и понятнее, чем есть на самом деле. Позднее они могут вернуться к модулю и удивиться, что могли написать нечто настолько ужасное. Чтобы это предотвравить, разработчики должны представлять себя на месте будущих читателей этого кода и приложить усилия для необходимого рефакторинга кода, так чтобы будущие читатели этого кода смогли его понять. Кроме того, желательно, чтобы кто-то еще просмотрел составленный ими код (code review).
  12. Таким образом, после того, как мы перечислили признаки плохого дизайна, мы можем инвертировать их и получить признаки хорошего дизайна. Гибкость – система легко поддается изменениям Прочность – единичное изменение не приводит к появлению неожиданных ошибок в зависимых модулях Переиспользуемость – систему легко разделить на компоненты, которые могут быть повторно использованы в других местах Отсутствие вязкости - легко внести изменения, не ухудшая дизайн проекта, не используя хаки Простота  – система не содержит частей, которые просто усложняют и замусоривают код и скорее всего никогда не понадобятся Отсутствие дублирования кода – код легко изменяется Читабельность - архитектура и код проекта легки для понимания Как же добиться, отсутствия признаков плохого дизайна и увидеть только признаки хорошего? Есть несколько практик, которые помогут в этом.
  13. SOLID - это аббревиатура пяти основных принципов дизайна классов в ООП. Понятие ввел Роберт Мартин в 2006 году в книге : «Принципы, паттерны и методики гибкой разработки на языке C#» S – Принципе единственной ответственности O – Принцип открытости/закрытости L - Принцип замещения Лисков I - Принцип разделения интерфейса D - Принцип инверсии зависимости
  14. Формулировка: не должно быть больше одной причины для изменения класса Что является причиной изменения логики работы класса? Видимо, изменение отношений между классами, введение новых требований или отмена старых. Вообще, вопрос о причине этих изменений лежит в плоскости ответственности, которую мы возложили на наш класс. Если у объекта много ответственности, то и меняться он будет очень часто. Таким образом, если класс имеет больше одной ответственности, то это ведет к хрупкости дизайна и ошибкам в неожиданных местах при изменениях кода.
  15. Формулировка: программные сущности (классы, модули, функции и т.д.) должны быть открыты для расширения, но закрыты для изменения Какую цель мы преследуем, когда применяем этот принцип? Как известно программные проекты в течение свой жизни постоянно изменяются. Изменения могут возникнуть, например, из-за новых требований заказчика или пересмотра старых. В конечном итоге потребуется изменить код в соответствии с текущей ситуацией. С одной стороны внесение изменений требует времени программистов и тестировщиков, которое является очень дорогим ресурсом в производстве ПО. С другой, бизнес должен достаточно быстро реагировать на рыночные изменения и время здесь представляется очень важным конкурентным преимуществом. Отсюда можно сделать вывод, что нашей целью является разработка системы, которая будет достаточно просто и безболезненно меняться. Другими словами, система должна быть гибкой. Например, внесение изменений в библиотеку общую для 4х проектов не должно быть долгим («долгим» является разным промежутком времени для конкретной ситуации) и уж точно не должно вести к изменениям в этих 4х проектах. Принцип открытости/закрытость как раз и дает понимание того, как оставаться достаточно гибкими в условиях постоянно меняющихся требований.
  16. У нас есть иерархия объектов с абстрактным родительским классом Entity и класс Repository, который использует абстракцию. При этом вызывая метод Save у Repositoryмы строим логику в зависимости от типа входного параметра. Из кода видно, что объект Repository придется менять каждый раз, когда мы добавляем в иерархию объектов с базовым классом AbstractEntity новых наследников или удаляем существующих. Условные операторы будут множится в методе save() и тем самым усложнять его.
  17. Конкретизируя классы методом instanceof мы должны сразу понять, что что-то с нашим кодом не то… Чтобы решить данную проблему, необходимо логику сохранения конкретных классов из иерархии Entity вынести в конкретные классы Repository. Для этого мы должны выделить интерфейс Repository и создать хранилища BookRepository и AuthorRepository:
  18. Объекты в программе могут быть заменены их наследниками без изменения свойств программы Наследующий класс должен дополнять, а не замещать поведение базового класса. Я уже приводил код проверки абстракции на тип на примере нарушения принципа открытости/закрытости. Теперь мы видим, что класс Repository нарушает еще и принцип замещения Лисков. Дело в том, что внутри класса Repository мы оперируем не только абстрактной сущностью AbstractEntity, но и унаследованными типами. А это значит, что в данном случае подтипы AccountEntity и RoleEntity не могут быть заменены типом, от которого они унаследованы. По определению имеем нарушение. Надо заметить, что принципы проектирования взаимосвязаны. Нарушение одного из принципов скорее всего приведет к нарушению одного или нескольких других принципов.
  19. У нас есть класс Rectangle (Прямоугольник). Мы можем задать/узнать ширину и высоту. А так же есть метод для получения площади прямоугольника. И есть частный случай прямоугольника – квадрат. Тут ширина и высота одинакова. Реализовать его предлагается через переопределение методов задания высоты и ширины, таким образом гарантируется, что фигура всегда будет квадратом. Давайте рассмотрим тест. Тест работает с прямоугольником, но при инициализации, мы все таки видим, что это частный случай прямоугольника – квадрат. При попытке подсчитать его площадь мы получим ошибку. Хотя, тест делает вполне разумные действия с прямоугольником, в рамках его контракта. Мы видим, что в данном случае, принцип Liskov не выполняется, нельзя будет в системе заменить Прямоугольник – Квадратом, без изменения свойств системы.
  20. У нас есть класс Rectangle (Прямоугольник). Мы можем задать/узнать ширину и высоту. А так же есть метод для получения площади прямоугольника. И есть частный случай прямоугольника – квадрат. Тут ширина и высота одинакова. Реализовать его предлагается через переопределение методов задания высоты и ширины, таким образом гарантируется, что фигура всегда будет квадратом. Давайте рассмотрим тест. Тест работает с прямоугольником, но при инициализации, мы все таки видим, что это частный случай прямоугольника – квадрат. При попытке подсчитать его площадь мы получим ошибку. Хотя, тест делает вполне разумные действия с прямоугольником, в рамках его контракта. Мы видим, что в данном случае, принцип Liskov не выполняется, нельзя будет в системе заменить Прямоугольник – Квадратом, без изменения свойств системы.
  21. Принцип разделения интерфейса Много специализированных интерфейсов лучше, чем один универсальный. Или клиенты не должны зависеть от методов, которые они не используют. Как и при использовании других принципов проектирования классов мы пытаемся избавиться от ненужных зависимостей в коде, сделать код легко читаемым и легко изменяемым.
  22. Формулировка: Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Сейчас мы создадим и отрефакторим приложение. Будем двигаться по шагам и я покажу применение принципа инверсии зависимостей в действии.
  23. Наша система будет консольным приложением, которое занимается рассылкой отчетов. <Program class> Главный объект в нашей бизнес-логике – Reporter. <Reporter class> Устроен Reporter очень просто. Он просит CustomReportBuilder создать список отчетов, а потом один за другим отсылает их с помощью объекта EmailReportSender. Есть ли в этом коде проблемы? В подавляющем большинстве случаев зависит от того, кто и как этот код будет использовать, как часто он будет меняться и т.д. Но есть проблемы, которые очевидны уже сейчас. Тестируемость Как протестировать функцию SendReports? Давайте проверим поведение функции, когда  CustomReportBuilder не создал ни одного отчета. В этом случае она должна создать исключение NoReportsException <Report Test> Как в этом случае задать поведение объектов, которые использует Reporter? Мы же должны «сказать»  CustomReportBuilder'у вернуть пустой список, и тогда функция sendReports выбросит исключение. Но в текущей реализации Reporter'а сделать мы этого не можем. Получается, мы не можем задать такие входные данные, при которых sendReports выкинет исключение. Значит в данной реализации объект Reporter очень плохо поддается тестированию. Связанность Дело в том, что функция sendReports, кроме своей прямой обязанности, слишком много знает и умеет: знает, что именно  CustomReportBuilder будет создавать отчеты знает, что все отчеты надо отсылать через email с помощью EmailReportSender умеет создавать объект  CustomReportBuilder умеет создавать объект EmailReportSender Здесь нарушается принцип единственности ответственности. Проблема заключается в том, что в данный момент внутри функции sendReports объект  CustomReportBuilder создается оператором new. А если у него появятся обязательные параметры в конструкторе? Нам придется менять код в классе Reporter да и во всех других классах, которые использовали оператор new для  CustomReportBuilder'а. К тому же, первые пункты нарушают принцип открытости/закрытости. Дело в том, что если мы захотим с помощью нашей утилиты отсылать сообщения через SMS, то придется изменять код класса Reporter. Вместо EmailReportSender мы должны будем написать SmsReportSender. Еще сложнее ситуация, когда одна часть пользователей класса Reporter захочет отправлять сообщения через emal, а вторая через SMS. Обратите внимание, что наш объект Reporter зависит не от абстракций, а от конкретных объектов  CustomReportBuilder и EmailReportSender. Можно сказать, что он "сцеплен" с этими классами. Это и объясняет его хрупкость при изменениях в системе. Может оказаться, что Reporter жестко зависит от двух классов, эти два класса зависят еще от 4х других. Получится, что вся система – это клубок из стальных ниток, который нельзя ни изменить, ни протестировать. Этот пример наглядно показывает нарушение принципа инверсии зависимостей.
  24. Наша система будет консольным приложением, которое занимается рассылкой отчетов. <Program class> Главный объект в нашей бизнес-логике – Reporter. <Reporter class> Устроен Reporter очень просто. Он просит CustomReportBuilder создать список отчетов, а потом один за другим отсылает их с помощью объекта EmailReportSender. Есть ли в этом коде проблемы? В подавляющем большинстве случаев зависит от того, кто и как этот код будет использовать, как часто он будет меняться и т.д. Но есть проблемы, которые очевидны уже сейчас. Тестируемость Как протестировать функцию SendReports? Давайте проверим поведение функции, когда  CustomReportBuilder не создал ни одного отчета. В этом случае она должна создать исключение NoReportsException <Report Test> Как в этом случае задать поведение объектов, которые использует Reporter? Мы же должны «сказать»  CustomReportBuilder'у вернуть пустой список, и тогда функция sendReports выбросит исключение. Но в текущей реализации Reporter'а сделать мы этого не можем. Получается, мы не можем задать такие входные данные, при которых sendReports выкинет исключение. Значит в данной реализации объект Reporter очень плохо поддается тестированию. Связанность Дело в том, что функция sendReports, кроме своей прямой обязанности, слишком много знает и умеет: знает, что именно  CustomReportBuilder будет создавать отчеты знает, что все отчеты надо отсылать через email с помощью EmailReportSender умеет создавать объект  CustomReportBuilder умеет создавать объект EmailReportSender Здесь нарушается принцип единственности ответственности. Проблема заключается в том, что в данный момент внутри функции sendReports объект  CustomReportBuilder создается оператором new. А если у него появятся обязательные параметры в конструкторе? Нам придется менять код в классе Reporter да и во всех других классах, которые использовали оператор new для  CustomReportBuilder'а. К тому же, первые пункты нарушают принцип открытости/закрытости. Дело в том, что если мы захотим с помощью нашей утилиты отсылать сообщения через SMS, то придется изменять код класса Reporter. Вместо EmailReportSender мы должны будем написать SmsReportSender. Еще сложнее ситуация, когда одна часть пользователей класса Reporter захочет отправлять сообщения через emal, а вторая через SMS. Обратите внимание, что наш объект Reporter зависит не от абстракций, а от конкретных объектов  CustomReportBuilder и EmailReportSender. Можно сказать, что он "сцеплен" с этими классами. Это и объясняет его хрупкость при изменениях в системе. Может оказаться, что Reporter жестко зависит от двух классов, эти два класса зависят еще от 4х других. Получится, что вся система – это клубок из стальных ниток, который нельзя ни изменить, ни протестировать. Этот пример наглядно показывает нарушение принципа инверсии зависимостей.
  25. Наша система будет консольным приложением, которое занимается рассылкой отчетов. <Program class> Главный объект в нашей бизнес-логике – Reporter. <Reporter class> Устроен Reporter очень просто. Он просит CustomReportBuilder создать список отчетов, а потом один за другим отсылает их с помощью объекта EmailReportSender. Есть ли в этом коде проблемы? В подавляющем большинстве случаев зависит от того, кто и как этот код будет использовать, как часто он будет меняться и т.д. Но есть проблемы, которые очевидны уже сейчас. Тестируемость Как протестировать функцию SendReports? Давайте проверим поведение функции, когда  CustomReportBuilder не создал ни одного отчета. В этом случае она должна создать исключение NoReportsException <Report Test> Как в этом случае задать поведение объектов, которые использует Reporter? Мы же должны «сказать»  CustomReportBuilder'у вернуть пустой список, и тогда функция sendReports выбросит исключение. Но в текущей реализации Reporter'а сделать мы этого не можем. Получается, мы не можем задать такие входные данные, при которых sendReports выкинет исключение. Значит в данной реализации объект Reporter очень плохо поддается тестированию. Связанность Дело в том, что функция sendReports, кроме своей прямой обязанности, слишком много знает и умеет: знает, что именно  CustomReportBuilder будет создавать отчеты знает, что все отчеты надо отсылать через email с помощью EmailReportSender умеет создавать объект  CustomReportBuilder умеет создавать объект EmailReportSender Здесь нарушается принцип единственности ответственности. Проблема заключается в том, что в данный момент внутри функции sendReports объект  CustomReportBuilder создается оператором new. А если у него появятся обязательные параметры в конструкторе? Нам придется менять код в классе Reporter да и во всех других классах, которые использовали оператор new для  CustomReportBuilder'а. К тому же, первые пункты нарушают принцип открытости/закрытости. Дело в том, что если мы захотим с помощью нашей утилиты отсылать сообщения через SMS, то придется изменять код класса Reporter. Вместо EmailReportSender мы должны будем написать SmsReportSender. Еще сложнее ситуация, когда одна часть пользователей класса Reporter захочет отправлять сообщения через emal, а вторая через SMS. Обратите внимание, что наш объект Reporter зависит не от абстракций, а от конкретных объектов  CustomReportBuilder и EmailReportSender. Можно сказать, что он "сцеплен" с этими классами. Это и объясняет его хрупкость при изменениях в системе. Может оказаться, что Reporter жестко зависит от двух классов, эти два класса зависят еще от 4х других. Получится, что вся система – это клубок из стальных ниток, который нельзя ни изменить, ни протестировать. Этот пример наглядно показывает нарушение принципа инверсии зависимостей.
  26. Применяем принцип инверсии зависимостей Сейчас несколькими простыми действиями мы решим наши проблемы с Reporter'ом. Для начала вынесем интерфейсы ReportSender из EmailReportSender и ReportBuilder из  CustomReportBuilder. Теперь вместо того, чтобы создавать объекты в функции SendReports, мы передами их объекту Reporter в конструктор
  27. Применяем принцип инверсии зависимостей Сейчас несколькими простыми действиями мы решим наши проблемы с Reporter'ом. Для начала вынесем интерфейсы ReportSender из EmailReportSender и ReportBuilder из  CustomReportBuilder. Теперь вместо того, чтобы создавать объекты в функции SendReports, мы передами их объекту Reporter в конструктор
  28. Теперь у нас есть возможность передавать в конструктор Reporter'а объекты, которые реализуют нужные интерфейсы. Давайте подставим mock-объекты и зададим нужное нам поведение. Тест прошел! Мы отлично справились. Теперь есть возможность задавать поведение объектов, с которыми работает наш Reporter. И в данном случае нам не важно, что где-то есть EmailReportSender, SmsReportSender или еще какой-то *ReportSender. Тесты Reporter'а не зависят от других реализаций, мы используем только интерфейсы. Это делает тесты более устойчивыми к изменениям в системе.