Рефакторинг и объектно-ориентированный дизайнIngate Development2011
Обращаться с языком кое-как — значит и мыслить кое-как: приблизительно, неточно, неверно.А. Н. Толстой.Код программы чаще читается, чем пишется.Народная мудрость.Цель: узнать, что такое правильный объектно-ориентированный дизайн программного обеспечения и какими способами его можно достигать.Ingate Development \  2011
О чем поговорим?Обсудим, что такое правильный дизайн и как его добиться.
Ответ на вопрос «что» дают принципы объектно-ориентированного дизайна и паттерны.
Ответ на вопрос «как» дают техники рефакторинга.
Будем в первую очередь разбираться в причинах того или иного варианта определения объектов и их взаимодействия.
Поговорим о том, что идеального дизайна не существует и каждому из вас предстоит нелегкая задача поиска баланса между нарушениями разных принципов.Ingate Development \  2011
О чем не будем говорить?Не будем разбирать конкретные паттерны объектно-ориентированного проектирования
Не будем перечислять конкретные техники рефакторинга
Хотя и паттерны и рефакторинг будем часто использовать в примерах.
Не будем разбирать принципы объектно-ориентированного подхода.
Не будем анализировать плюсы и минусы объектно-ориентированного подхода. Будем считать, что раз вы сюда пришли, то выбор вы для себя уже сделали.Ingate Development \  2011
ОО дизайнSOLID – этоаббревиатура пяти основных принципов дизайна в объектно-ориентированном проектировании. В дополнение к этому, рассмотрим еще шестой принцип, который не входит в эту аббревиатуру.Ingate Development \  2011
РефакторингРефакторинг (англ. refactoring) — процесс изменения внутренней структуры программы, не затрагивающий её внешнего поведения и имеющий целью облегчить понимание её работы.В основе рефакторинга лежит последовательность небольших эквивалентных (то есть сохраняющих поведение) преобразований.Поскольку каждое преобразование маленькое, программисту легче проследить за его правильностью, и в то же время вся последовательность может привести к существенной перестройке программы и улучшению её согласованности и четкости.WikipediaIngate Development \  2011
Single Responsibility PrincipleПринцип единственной обязанностиIngate Development \  2011
Single Responsibility PrincipleВ объектно-ориентированном программировании, принцип единственной обязанности обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс.WikipediaНе должно существовать более одного мотива для изменения класса.Роберт МартинIngate Development \  2011
Single Responsibility PrincipleIngate Development \  2011
Single Responsibility PrincipleСвязность (cohesion):насколько сильно связаны и сонаправлены различные обязанности модуляСвязанность (coupling):степень, в которой программный модуль зависит от других модулейСтремитесь к слабой связанности, но к сильной связности модулейIngate Development \  2011
Single Responsibility PrincipleИзменения в требованиях обычно влекут за собой изменение обязанностей
Чем больше обязанностей у класса – тем больше вероятность его изменения
Несколько обязанностей в пределах одного класса делают эти обязанности взаимозависимыми
Чем больше классов затронет изменение, тем больше ошибок при этом наделаютIngate Development \  2011
Single Responsibility PrincipleПример нарушения принципаIngate Development \  2011
Single Responsibility PrincipleОписание проблемыОплата наличными не требует операций с кредитками
Операции в розничной системе продаж не требуют резервирования товара на складе
Операции на сайте не требуют отчисления бонусов торговому менеджеру
При этом любые изменения в систему процессинга платежей, систему резервирования товаров или начисления бонусов затрагивает Order, а соответственно и оба модуля Web и SaleSystemIngate Development \  2011
Single Responsibility PrincipleРефакторингIngate Development \  2011
Open/Closed PrincipleПринцип открытости/закрытостиIngate Development \  2011
Open/Closed PrincipleСущности в программе (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения. WikipediaIngate Development \  2011
Open/Closed PrincipleIngate Development \  2011
Open/Closed PrincipleОткрыты для расширенияНовая функциональность может быть добавлена в будущемЗакрыты для измененияИзменения в исходниках или бинарных сборках не требуются для добавления функциональностиПринцип впервые описан Бертраном Мейером в 1988 г.Ingate Development \  2011
Open/Closed PrincipleПример нарушения принципаIngate Development \  2011
Open/Closed PrincipleОписание проблемыДобавление новых правил каждый раз требует изменения калькулятора
Каждое изменение может наплодить багов и требует повторного тестирования
Добавление новых классов в меньшей степени чревато проблемами:
Нет кода, который зависит от новых классов (пока)
Новые классы не имеют исторической связанности, которая затрудняет их проектирование и тестированиеIngate Development \  2011
Open/Closed PrincipleТри подхода к достижению OCPИспользование параметров
Дайте клиентам возможность контролировать поведение через дополнительные параметры
В сочетании с делегатами/лямбда-выражениями можно добиться очень хорошего результата
Использование наследования
Потомки определяют (доопределяют) поведение базового класса / интерфейса
Использование паттерна «Стратегия»
Клиентский код зависит от абстракции
Используется  модель «plug in», которая позволяет переопределять поведение, заменив абстракциюIngate Development \  2011
Open/Closed PrincipleРефакторингIngate Development \  2011
Open/Closed PrincipleПонимание приходит с опытомЕсли ваш собственный опыт в предметной области подсказывает вам, что определенные изменения будут повторяться – примените OCP сразу при проектировании
Если модуль изменяется в первый раз – нарушьте принцип и просто измените реализацию
Если модуль изменяется повторно – примените рефакторинг для достижения OCP
Бесплатный сыр бывает только в мышеловке:OCP всегда усложняет дизайн
И помните:нет такого дизайна, который бы был закрыт от всех измененийIngate Development \  2011
Liskov substitution principleПринцип подстановки ЛисковойIngate Development \  2011
Liskov substitution principleФункции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.Роберт МартинНазван так в честь Барбары Лисковой, которая описала этот принцип впервые в 1988 г.Ingate Development \  2011
Liskov substitution principleIngate Development \  2011
Liskov substitution principleПотомки (дочерние классы) не должны:убирать поведение предков
нарушать контрактные обязательства предковИ вообще код при использовании потомков не должен знать, что он оперирует не предком, а чем-то еще.Ingate Development \  2011
Liskov substitution principleОбычнопод наследованием понимают отношение«ISA»
Strawberry IS A berry
Клубника – это ягода
С точки зрения принципа подстановки Лисковой правильнее говорить об отношении«IS SUBSTITUTABLE FOR»
Strawberry IS SUBSTITUTABLE FOR berry
Клубника может быть использована как ягода
И тогда не будет неожиданностей…
Волчья ягода – это ягода
Волчья ягода может быть использована как ягодаIngate Development \  2011
Liskov substitution principleТипичное нарушение принципаabstractclassBase {publicabstractvoid Method1();publicabstractvoid Method2();}classDerived : Base {publicoverridevoid Method1() {thrownewNotImplementedException();        } publicoverridevoid Method2() {// do something}} Ingate Development \  2011
Liskov substitution principleТипичное нарушение принципаpublicvoidPrint(Employeeperson) {if(person isProgrammer) {_printer.PrintProgrammer(person);} elseif (person isManager) {_printer.PrintManager(person);}} Ingate Development \  2011

Принципы объектно-ориентированного дизайна