OO Design with C++: 5. Design Principles, part 2

954 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
954
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

OO Design with C++: 5. Design Principles, part 2

  1. 1. C++ : принципы проектирования, часть 2 Дмитрий Штилерман, Рексофт
  2. 2. Основные принципы проектирования иерархий классов <ul><li>The Open-Closed Principle </li></ul><ul><li>The Liskov Substitution Principle </li></ul><ul><li>Design By Contract </li></ul><ul><li>The Dependency Inversion Principle </li></ul>
  3. 3. The Open-Closed Principle (OCP) <ul><li>Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. (Bertrand Meyer) </li></ul><ul><li>“ Open for extension” - п оведение модуля может быть расширено в свете новых требований. </li></ul><ul><li>“ Closed for modification” - исходный код модуля не должен меняться. </li></ul>
  4. 4. OCP - простой конкретный пример (1) struct Shape {ShapeType type; Rect bounds;}; void DrawShapes(Shape shapes[], int count) { for(int i = 0; i < count; i++) switch(shapes[i]) { case Circle: DrawCircle(shapes[i].bounds); break; case Rectangle: DrawRectangle(shapes[i].bounds); break; } } Процедурный стиль. Как добавить новый тип фигуры? ???
  5. 5. <ul><li>ОО-стиль - полиморфизм. </li></ul>OCP - простой конкретный пример (2) class Shape {public: virtual void draw() = 0;}; class Circle : public Shape {void draw();}; class Rectangle : public Shape {void draw();}; void DrawShapes(std::vector<Shape*>& v) { std::vector<Shape*>::iterator itr; for(itr = v.begin(); itr != v.end(); itr++) (*itr)->draw(); } Добавление нового типа фигуры не меняет старый код.
  6. 6. OCP - простой абстрактный пример Поведение клиента зависит от конкретного класса сервера и не может быть изменено без изменения исходного кода клиента Поведение клиента может быть изменено путем использования разных реализаций сервера
  7. 7. OCP - strategic closure (1) <ul><li>Модуль не может быть полностью закрыт для модификации! </li></ul>class Shape {public: virtual void draw() = 0;}; class Circle : public Shape {void draw();}; class Rectangle : public Shape {void draw();}; void DrawShapes(std::vector<Shape*>& v) { std::vector<Shape*>::iterator itr; for(itr = v.begin(); itr != v.end(); itr++) (*itr)->draw(); } Новое требование: все круги рисовать до всех прямоугольников. ???
  8. 8. OCP - strategic closure (2) <ul><li>Дизайнер должен решить, от каких будущих изменений требований должен быть «закрыт» модуль. </li></ul><ul><li>Основания для решения: знание предметной области и/или заказчика и оценка вероятности того или иного класса изменений. </li></ul><ul><li>Важно: способ «закрытия» оказывает влияние на способ «открытия». </li></ul>
  9. 9. The Liskov Substitution Principle (LSP) <ul><li>An object-oriented function that uses pointers or references to a base class must be able to use objects of derived classes without knowing it. (Barbara Liskov, цит. по Robert C. Martin) </li></ul><ul><li>Родственная концепция: Design By Contract (Bertrand Meyer) </li></ul>
  10. 10. Пример н арушения LSP - RTTI class Person {}; class Student : public Person {}; class Teacher : public Person {}; void processStudent(Student& student); void processTeacher(Teacher& teacher); void processPerson(Person& person) { if(typeid(person) == typeid(Student)) processStudent(static_cast<Student&>(person)); else if(typeid(person) == typeid(Teacher)) processTeacher(static_cast<Teacher&>(person)); } class SysAdmin : public Person {}; processPerson is broken!
  11. 11. Пример н арушения LSP - нарушение контракта class Rectangle { public: int getHeight() const; virtual void setHeight(int); int getWidth() const; virtual void setWidth(int); }; void RectangleTest(Rectangle& r) { r.setHeight(4); r.setWidth(5); int S = r.getHeight()*r.getWidth(); assert(S==20); } class Square : public Rectangle { public: void setHeight(int h) { Rectangle::setHeight(h); Rectangle::setWidth(h); } void setWidth(int w) { Rectangle::setWidth(w); Rectangle::setHeight(w); } };
  12. 12. Design By Contract (DBC) <ul><li>A routine (i.e. a method or a function) declares its preconditions and postconditions. The preconditions must be true in order for the routine to execute. Upon completion, the routine guarantees that the postcondition will be true. </li></ul><ul><li>When redefining a routine in a derivative (i.e. a method in a derived class), you may only replace its precondition by a weaker one, and its postcondition by a stronger one (“contract rule for derivatives”). This rule is logically equivalent to LSP. </li></ul><ul><li>Bertrand Meyer, “Object-Oriented Software Construction” </li></ul>
  13. 13. DBC - анализ примера class Rectangle { public: int getHeight() const; virtual void setHeight(int); int getWidth() const; virtual void setWidth(int); }; class Square : public Rectangle { public: void setHeight(int h) { Rectangle::setHeight(h); Rectangle::setWidth(h); } void setWidth(int w) { Rectangle::setWidth(w); Rectangle::setHeight(w); } }; Square::setHeight(h) postcondition: getHeight() == h getWidth() == h Square::setWidth(w) postcondition: getWidth() == w getHeight() == w Rectangle::setHeight(h) postcondition: getHeight() == h getWidth() unchanged Rectangle::setWidth(w) postcondition: getWidth() == w getHeight() unchanged
  14. 14. The Dependency Inversion Principle (DIP) <ul><li>High level modules should not depend upon low level modules. Both should depend upon abstractions. </li></ul><ul><li>Abstractions should not depend upon details. Details should depend upon abstractions. </li></ul><ul><li>Robert C. Martin, “The Dependency Inversion Principle” </li></ul>
  15. 15. DIP - простой пример Клиент напрямую зависит от класса сервера и не может быть использован отдельно от него. Клиент зависит от абстрактного интерфейса Почему dependency inversion? Простой ответ: разворот стрелочек у серверных классов. Более сложный ответ: см ниже.
  16. 16. DIP - проектирование многоуровневых систем (1) <ul><li>Высшие уровни зависят от нижних. </li></ul><ul><li>Изменения в нижних уровнях диктуют изменения в высших. </li></ul><ul><li>Повторное использование высших уровней невозможно без использования нижних. </li></ul>
  17. 17. DIP - проектирование многоуровневых систем (2) DIP позволяет решить описанные проблемы.
  18. 18. The Interface Segregation Principle (ISP) <ul><li>Clients should not be forced to depend upon interfaces that they do not use. </li></ul><ul><li>Robert C. Martin, “The Interface Segregation Principle” </li></ul><ul><li>… to be continued … </li></ul>

×