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

8,034 views
7,862 views

Published on

Published in: Education
0 Comments
13 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
8,034
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
0
Comments
0
Likes
13
Embeds 0
No embeds

No notes for slide

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

  1. 1. Рефакторинг и объектно-ориентированный дизайн<br />Ingate Development<br />2011<br />
  2. 2. Обращаться с языком кое-как — значит и мыслить кое-как: приблизительно, неточно, неверно.<br />А. Н. Толстой.<br />Код программы чаще читается, чем пишется.<br />Народная мудрость.<br />Цель: узнать, что такое правильный объектно-ориентированный дизайн программного обеспечения и какими способами его можно достигать.<br />Ingate Development 2011<br />
  3. 3. О чем поговорим?<br /><ul><li>Обсудим, что такое правильный дизайн и как его добиться.
  4. 4. Ответ на вопрос «что» дают принципы объектно-ориентированного дизайна и паттерны.
  5. 5. Ответ на вопрос «как» дают техники рефакторинга.
  6. 6. Будем в первую очередь разбираться в причинах того или иного варианта определения объектов и их взаимодействия.
  7. 7. Поговорим о том, что идеального дизайна не существует и каждому из вас предстоит нелегкая задача поиска баланса между нарушениями разных принципов.</li></ul>Ingate Development 2011<br />
  8. 8. О чем не будем говорить?<br /><ul><li>Не будем разбирать конкретные паттерны объектно-ориентированного проектирования
  9. 9. Не будем перечислять конкретные техники рефакторинга
  10. 10. Хотя и паттерны и рефакторинг будем часто использовать в примерах.
  11. 11. Не будем разбирать принципы объектно-ориентированного подхода.
  12. 12. Не будем анализировать плюсы и минусы объектно-ориентированного подхода. Будем считать, что раз вы сюда пришли, то выбор вы для себя уже сделали.</li></ul>Ingate Development 2011<br />
  13. 13. ОО дизайн<br />SOLID – этоаббревиатура пяти основных принципов дизайна в объектно-ориентированном проектировании. В дополнение к этому, рассмотрим еще шестой принцип, который не входит в эту аббревиатуру.<br />Ingate Development 2011<br />
  14. 14. Рефакторинг<br />Рефакторинг (англ. refactoring) — процесс изменения внутренней структуры программы, не затрагивающий её внешнего поведения и имеющий целью облегчить понимание её работы.<br />В основе рефакторинга лежит последовательность небольших эквивалентных (то есть сохраняющих поведение) преобразований.<br />Поскольку каждое преобразование маленькое, программисту легче проследить за его правильностью, и в то же время вся последовательность может привести к существенной перестройке программы и улучшению её согласованности и четкости.<br />Wikipedia<br />Ingate Development 2011<br />
  15. 15. Single Responsibility Principle<br />Принцип единственной обязанности<br />Ingate Development 2011<br />
  16. 16. Single Responsibility Principle<br />В объектно-ориентированном программировании, принцип единственной обязанности обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс.<br />Wikipedia<br />Не должно существовать более одного мотива для изменения класса.<br />Роберт Мартин<br />Ingate Development 2011<br />
  17. 17. Single Responsibility Principle<br />Ingate Development 2011<br />
  18. 18. Single Responsibility Principle<br />Связность (cohesion):насколько сильно связаны и сонаправлены различные обязанности модуля<br />Связанность (coupling):степень, в которой программный модуль зависит от других модулей<br />Стремитесь к слабой связанности, но к сильной связности модулей<br />Ingate Development 2011<br />
  19. 19. Single Responsibility Principle<br /><ul><li>Изменения в требованиях обычно влекут за собой изменение обязанностей
  20. 20. Чем больше обязанностей у класса – тем больше вероятность его изменения
  21. 21. Несколько обязанностей в пределах одного класса делают эти обязанности взаимозависимыми
  22. 22. Чем больше классов затронет изменение, тем больше ошибок при этом наделают</li></ul>Ingate Development 2011<br />
  23. 23. Single Responsibility Principle<br />Пример нарушения принципа<br />Ingate Development 2011<br />
  24. 24. Single Responsibility Principle<br />Описание проблемы<br /><ul><li>Оплата наличными не требует операций с кредитками
  25. 25. Операции в розничной системе продаж не требуют резервирования товара на складе
  26. 26. Операции на сайте не требуют отчисления бонусов торговому менеджеру
  27. 27. При этом любые изменения в систему процессинга платежей, систему резервирования товаров или начисления бонусов затрагивает Order, а соответственно и оба модуля Web и SaleSystem</li></ul>Ingate Development 2011<br />
  28. 28. Single Responsibility Principle<br />Рефакторинг<br />Ingate Development 2011<br />
  29. 29. Open/Closed Principle<br />Принцип открытости/закрытости<br />Ingate Development 2011<br />
  30. 30. Open/Closed Principle<br />Сущности в программе (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для изменения. <br />Wikipedia<br />Ingate Development 2011<br />
  31. 31. Open/Closed Principle<br />Ingate Development 2011<br />
  32. 32. Open/Closed Principle<br />Открыты для расширения<br />Новая функциональность может быть добавлена в будущем<br />Закрыты для изменения<br />Изменения в исходниках или бинарных сборках не требуются для добавления функциональности<br />Принцип впервые описан Бертраном Мейером в 1988 г.<br />Ingate Development 2011<br />
  33. 33. Open/Closed Principle<br />Пример нарушения принципа<br />Ingate Development 2011<br />
  34. 34. Open/Closed Principle<br />Описание проблемы<br /><ul><li>Добавление новых правил каждый раз требует изменения калькулятора
  35. 35. Каждое изменение может наплодить багов и требует повторного тестирования
  36. 36. Добавление новых классов в меньшей степени чревато проблемами:
  37. 37. Нет кода, который зависит от новых классов (пока)
  38. 38. Новые классы не имеют исторической связанности, которая затрудняет их проектирование и тестирование</li></ul>Ingate Development 2011<br />
  39. 39. Open/Closed Principle<br />Три подхода к достижению OCP<br /><ul><li>Использование параметров
  40. 40. Дайте клиентам возможность контролировать поведение через дополнительные параметры
  41. 41. В сочетании с делегатами/лямбда-выражениями можно добиться очень хорошего результата
  42. 42. Использование наследования
  43. 43. Потомки определяют (доопределяют) поведение базового класса / интерфейса
  44. 44. Использование паттерна «Стратегия»
  45. 45. Клиентский код зависит от абстракции
  46. 46. Используется модель «plug in», которая позволяет переопределять поведение, заменив абстракцию</li></ul>Ingate Development 2011<br />
  47. 47. Open/Closed Principle<br />Рефакторинг<br />Ingate Development 2011<br />
  48. 48. Open/Closed Principle<br />Понимание приходит с опытом<br /><ul><li>Если ваш собственный опыт в предметной области подсказывает вам, что определенные изменения будут повторяться – примените OCP сразу при проектировании
  49. 49. Если модуль изменяется в первый раз – нарушьте принцип и просто измените реализацию
  50. 50. Если модуль изменяется повторно – примените рефакторинг для достижения OCP
  51. 51. Бесплатный сыр бывает только в мышеловке:OCP всегда усложняет дизайн
  52. 52. И помните:нет такого дизайна, который бы был закрыт от всех изменений</li></ul>Ingate Development 2011<br />
  53. 53. Liskov substitution principle<br />Принцип подстановки Лисковой<br />Ingate Development 2011<br />
  54. 54. Liskov substitution principle<br />Функции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.<br />Роберт Мартин<br />Назван так в честь Барбары Лисковой, которая описала этот принцип впервые в 1988 г.<br />Ingate Development 2011<br />
  55. 55. Liskov substitution principle<br />Ingate Development 2011<br />
  56. 56. Liskov substitution principle<br />Потомки (дочерние классы) не должны:<br /><ul><li>убирать поведение предков
  57. 57. нарушать контрактные обязательства предков</li></ul>И вообще код при использовании потомков не должен знать, что он оперирует не предком, а чем-то еще.<br />Ingate Development 2011<br />
  58. 58. Liskov substitution principle<br /><ul><li>Обычнопод наследованием понимают отношение«ISA»
  59. 59. Strawberry IS A berry
  60. 60. Клубника – это ягода
  61. 61. С точки зрения принципа подстановки Лисковой правильнее говорить об отношении«IS SUBSTITUTABLE FOR»
  62. 62. Strawberry IS SUBSTITUTABLE FOR berry
  63. 63. Клубника может быть использована как ягода
  64. 64. И тогда не будет неожиданностей…
  65. 65. Волчья ягода – это ягода
  66. 66. Волчья ягода может быть использована как ягода</li></ul>Ingate Development 2011<br />
  67. 67. Liskov substitution principle<br />Типичное нарушение принципа<br />abstractclassBase {<br />publicabstractvoid Method1();<br />publicabstractvoid Method2();<br />}<br />classDerived : Base {<br />publicoverridevoid Method1() {<br />thrownewNotImplementedException();<br /> }<br /> <br />publicoverridevoid Method2() {<br />// do something<br />}<br />}<br /> <br />Ingate Development 2011<br />
  68. 68. Liskov substitution principle<br />Типичное нарушение принципа<br />publicvoidPrint(Employeeperson) {<br />if(person isProgrammer) {<br />_printer.PrintProgrammer(person);<br />} elseif (person isManager) {<br />_printer.PrintManager(person);<br />}<br />} <br />Ingate Development 2011<br />
  69. 69. Liskov substitution principle<br />Пример нарушения принципа<br />Ingate Development 2011<br />
  70. 70. Liskov substitution principle<br />Описание проблемы<br /><ul><li>Клиентский код знает слишком много об устройстве используемого класса
  71. 71. Доработки в коде сводятся к добавлению if-then или switch конструкций, что в свою очередь ведет к кошмару при поддержке данного кода
  72. 72. Налицо нарушение принципа OCP</li></ul>Ingate Development 2011<br />
  73. 73. Liskov substitution principle<br />Рефакторинг<br />Ingate Development 2011<br />
  74. 74. Interface segregation principle<br />Принцип изолированности интерфейса<br />Ingate Development 2011<br />
  75. 75. Interface segregation principle<br />Клиент не должен вынужденно зависеть от элементов интерфейса, которые он не использует.<br />Роберт Мартин<br />Смысл: предпочитайте маленькие, простые интерфейсы «жирным».<br />Ingate Development 2011<br />
  76. 76. Interface segregation principle<br />Ingate Development 2011<br />
  77. 77. Interface segregation principle<br />Под интерфейсом здесь нужно понимать:<br /><ul><li>Интерфейс, описанный ключевым словом interfacepublicinterfaceIDoSomething{}
  78. 78. Публичный интерфейс классаpublicclassSomeClass{}</li></ul>То есть то, что клиент видит и может использовать.<br />Ingate Development 2011<br />
  79. 79. Interface segregation principle<br />Пример нарушения принципа<br />Ingate Development 2011<br />
  80. 80. Interface segregation principle<br />Типичное нарушение принципа<br />publicoverridevoidSomeMethod() {<br />thrownewNotImplementedException();<br />}<br />Что само по себе является нарушением LSP<br />Ingate Development 2011<br />
  81. 81. Interface segregation principle<br />Когда нужно применять рефакторинг?<br /><ul><li>Нет неудобства – нет проблемы
  82. 82. Если нет проблемы, то незачем и рефакторить
  83. 83. Если ваш код зависит от «жирного» интерфейса и к нему есть доступ
  84. 84. Создайте нужный маленький интерфейс
  85. 85. Свяжите его с «жирным»
  86. 86. Используйте новый интерфейс
  87. 87. Если ваш код зависит от «жирного» интерфейса и к нему нет доступа
  88. 88. Создайте нужный маленький интерфейс
  89. 89. Реализуйте нужные методы используя промежуточный класс, реализующий «жирный» интерфейс (паттерн Adapter)</li></ul>Ingate Development 2011<br />
  90. 90. Interface segregation principle<br />Рефакторинг<br />Ingate Development 2011<br />
  91. 91. Dependency inversion principle<br />Принцип инверсии зависимости<br />Ingate Development 2011<br />
  92. 92. Dependency inversion principle<br />Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций.<br />Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. <br />Роберт Мартин<br />Ingate Development 2011<br />
  93. 93. Dependency inversion principle<br />Ingate Development 2011<br />
  94. 94. Dependency inversion principle<br />Код обычно зависит от:<br /><ul><li>Framework
  95. 95. Сторонние библиотеки
  96. 96. База данных
  97. 97. Файловая система
  98. 98. Email
  99. 99. Web services
  100. 100. Системные ресурсы (часы, например)
  101. 101. Конфигурация
  102. 102. Ключевое слово new
  103. 103. Статические методы
  104. 104. Thread.Sleep()
  105. 105. Random</li></ul>Ingate Development 2011<br />
  106. 106. Dependency inversion principle<br />Традиционный подход<br /><ul><li>Модули более высокого уровня вызывают модули низшего уровня
  107. 107. Пользовательский интерфейс зависит от
  108. 108. Бизнес логики, которая зависит от
  109. 109. Инфраструктурных классов
  110. 110. Утилит / хелперов
  111. 111. Обертки доступа к данным (DAO)
  112. 112. Статические методы используются везде, потому что это удобно
  113. 113. Создание объектов разбросано по всем модулям
  114. 114. Типичное нарушение принципа SRP</li></ul>Ingate Development 2011<br />
  115. 115. Dependency inversion principle<br /><ul><li>Если конструктор требует определения всех зависимостей класса, то говорят о том, что класс имеет явныезависимости
  116. 116. Если же этого нет, то класс имеет скрытые зависимости </li></ul>publicclassHelloWorldHidden {<br />publicstring Hello(string name) {<br />if (DateTime.Now.Hour < 12) {<br />return"Good morning, " + name;<br /> }<br />if (DateTime.Now.Hour < 18) {<br />return"Good afternoon, " + name;<br /> }<br />return"Good evening, " + name;<br /> }<br /> }<br />Ingate Development 2011<br />
  117. 117. Dependency inversion principle<br />Подход DIP<br /><ul><li>Классы должны явно указывать то, что они используют</li></ul>publicclassHelloWorldExplicit {<br />privatereadonlyDateTime _timeOfGreeting;<br />publicHelloWorldExplicit(DateTimetimeOfGreeting) {<br /> _timeOfGreeting = timeOfGreeting;<br /> }<br />publicstring Hello(string name) {<br />if (_timeOfGreeting.Hour < 12) {<br />return"Good morning, " + name;<br /> }<br />if (_timeOfGreeting.Hour < 18) {<br />return"Good afternoon, " + name;<br /> }<br />return"Good evening, " + name;<br /> }<br /> }<br />Ingate Development 2011<br />
  118. 118. Dependency inversion principle<br />Пример нарушения принципа<br />Ingate Development 2011<br />
  119. 119. Dependency inversion principle<br />Описание проблемы<br /><ul><li>OnlineOrderимеет скрытые зависимости:
  120. 120. MailMessage
  121. 121. SmtpClient
  122. 122. InventorySystem
  123. 123. PaymentGateway
  124. 124. В результате
  125. 125. Высокая связанность (coupling)
  126. 126. Трудности с изменением поведения(нарушение принципа OCP)
  127. 127. Класс сложно тестировать</li></ul>Ingate Development 2011<br />
  128. 128. Dependency inversion principle<br />Внедрение зависимости<br /><ul><li>Данная техника означает, что вызывающий код может внедрить поведение в объект
  129. 129. Три основные техники
  130. 130. Внедрение через свойства
  131. 131. Внедрение через конструктор
  132. 132. Внедрение через параметры метода
  133. 133. Есть и другие техники, конечно</li></ul>Ingate Development 2011<br />
  134. 134. Dependency inversion principle<br />Внедрение через свойства<br /><ul><li>Плюсы
  135. 135. Поведение может быть заменено в любой момент жизни объекта
  136. 136. Поведение может быть настроено многими способами - очень гибкая методика
  137. 137. Минусы
  138. 138. Объекты могут быть в невалидном состоянии в промежуток времени между созданием и определением всех зависимостей через setter’ы
  139. 139. Не всегда интуитивно понятно поведение</li></ul>Ingate Development 2011<br />
  140. 140. Dependency inversion principle<br />Внедрение через конструктор<br /><ul><li>Плюсы
  141. 141. Классы явно указывают клиентам, от чего они зависят
  142. 142. Классы всегда находятся в валидном состоянии
  143. 143. Минусы
  144. 144. У конструкторов может быть слишком много параметров
  145. 145. Иногда (при сериализации, например)требуется конструктор по умолчанию
  146. 146. Некоторые методы могут не использовать все зависимости, описанные конструктором</li></ul>Ingate Development 2011<br />
  147. 147. Dependency inversion principle<br />Внедрение через параметры<br /><ul><li>Плюсы
  148. 148. Наиболее полное разделение поведения между методами
  149. 149. Очень гибкие возможности
  150. 150. Не требуется изменений всего класса
  151. 151. Минусы
  152. 152. Меняет сигнатуру методов
  153. 153. Количество параметров может быть неприлично большим
  154. 154. Если только один метод имеет зависимость, то используйте эту технику. Иначе – внедрение через конструктор</li></ul>Ingate Development 2011<br />
  155. 155. Dependency inversion principle<br />Рефакторинг<br />Ingate Development 2011<br />
  156. 156. Dependency inversion principle<br />Типичные нарушения принципа<br />foreach (var item incart.Items) {<br />varinventorySystem = new InventorySystem();<br />inventorySystem.Reserve(item);<br />}<br />var message = new MailMessage {<br />Subject = "Заказ принят в " + DateTime.Now<br />}; <br />Ingate Development 2011<br />
  157. 157. Dependency inversion principle<br />Где же создавать объекты?<br /><ul><li>Конструктор по умолчанию
  158. 158. Можно создавать все необходимые зависимости в конструкторе по умолчанию
  159. 159. При этом будут созданы те реализации, которые должны чаще всего использоваться в приложении
  160. 160. Такой вариант использования принципа называется«DIP для бедняков» (Poor man’s DIP)
  161. 161. ФукнцияMain
  162. 162. Можно создавать все, что вам нужно при старте программы
  163. 163. IoC Container
  164. 164. Можно использовать специализированную библиотеку</li></ul>Ingate Development 2011<br />
  165. 165. Dependency inversion principle<br />Microsoft Unity<br />Пример кода:<br />IUnityContainermyContainer = newUnityContainer();<br />myContainer.RegisterType<IMyService, CustomerService>();<br />IMyServicemyServiceInstance = myContainer.Resolve<IMyService>();<br />Ingate Development 2011<br />
  166. 166. Dependency inversion principle<br />Традиционная архитектура приложения<br />Ingate Development 2011<br />
  167. 167. Dependency inversion principle<br />Подход DIP<br />Ingate Development 2011<br />
  168. 168. Don’t repeat yourselfor Duplicate is evil<br />Не повторяйся илиДублирование - зло<br />Ingate Development 2011<br />
  169. 169. Don’t repeat yourself<br />Каждое отдельное понятие должно иметь единственное, однозначное отражение в системе.<br />Энди Хунт<br />Логические повторы говорят о необходимости абстракции. Повторы в работе говорят о необходимости автоматизации.<br />97 вещей, которые должен знать любой программист.<br />Ingate Development 2011<br />
  170. 170. Don’t repeat yourself<br />Ingate Development 2011<br />
  171. 171. Don’t repeat yourself<br />Пример нарушения принципа<br />Ingate Development 2011<br />
  172. 172. Don’t repeat yourself<br />Что является дублированием?<br /><ul><li>Магические константы
  173. 173. Дублирующаяся логика в разных частях кода
  174. 174. Повторяющаяся if-then логика
  175. 175. Однотипные операции
  176. 176. Повторяющиеся вычисления во время выполнения</li></ul>Ingate Development 2011<br />
  177. 177. Don’t repeat yourself<br />Рефакторинг<br />Ingate Development 2011<br />
  178. 178. Don’t repeat yourself<br />Повторения в процессе работы<br /><ul><li>Тестирование вручную – муторно и неэффективно
  179. 179. Сборка вручную – это муторно и неэффективно
  180. 180. Публикация вручную – это муторно и неэффективно
  181. 181. Если вы поняли суть этого принципа, то у вас должно возникнуть желание написатьstring.Format("{0} вручную – это муторно и неэффективно", action);</li></ul>Ingate Development 2011<br />
  182. 182. На этом все <br />Почитать еще что-нибудь: dev.ingate.ru<br />Задать мне вопрос: sergey.shebanin@ingate.ru<br />

×