• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
OO Design with C++: 5. Design Principles, part 2
 

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

on

  • 1,176 views

 

Statistics

Views

Total Views
1,176
Views on SlideShare
1,175
Embed Views
1

Actions

Likes
1
Downloads
0
Comments
0

1 Embed 1

http://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

    • C++ : принципы проектирования, часть 2 Дмитрий Штилерман, Рексофт
    • Основные принципы проектирования иерархий классов
      • The Open-Closed Principle
      • The Liskov Substitution Principle
      • Design By Contract
      • The Dependency Inversion Principle
    • The Open-Closed Principle (OCP)
      • Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. (Bertrand Meyer)
      • “ Open for extension” - п оведение модуля может быть расширено в свете новых требований.
      • “ Closed for modification” - исходный код модуля не должен меняться.
    • 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; } } Процедурный стиль. Как добавить новый тип фигуры? ???
      • ОО-стиль - полиморфизм.
      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(); } Добавление нового типа фигуры не меняет старый код.
    • OCP - простой абстрактный пример Поведение клиента зависит от конкретного класса сервера и не может быть изменено без изменения исходного кода клиента Поведение клиента может быть изменено путем использования разных реализаций сервера
    • OCP - strategic closure (1)
      • Модуль не может быть полностью закрыт для модификации!
      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(); } Новое требование: все круги рисовать до всех прямоугольников. ???
    • OCP - strategic closure (2)
      • Дизайнер должен решить, от каких будущих изменений требований должен быть «закрыт» модуль.
      • Основания для решения: знание предметной области и/или заказчика и оценка вероятности того или иного класса изменений.
      • Важно: способ «закрытия» оказывает влияние на способ «открытия».
    • The Liskov Substitution Principle (LSP)
      • 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)
      • Родственная концепция: Design By Contract (Bertrand Meyer)
    • Пример н арушения 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!
    • Пример н арушения 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); } };
    • Design By Contract (DBC)
      • 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.
      • 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.
      • Bertrand Meyer, “Object-Oriented Software Construction”
    • 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
    • The Dependency Inversion Principle (DIP)
      • High level modules should not depend upon low level modules. Both should depend upon abstractions.
      • Abstractions should not depend upon details. Details should depend upon abstractions.
      • Robert C. Martin, “The Dependency Inversion Principle”
    • DIP - простой пример Клиент напрямую зависит от класса сервера и не может быть использован отдельно от него. Клиент зависит от абстрактного интерфейса Почему dependency inversion? Простой ответ: разворот стрелочек у серверных классов. Более сложный ответ: см ниже.
    • DIP - проектирование многоуровневых систем (1)
      • Высшие уровни зависят от нижних.
      • Изменения в нижних уровнях диктуют изменения в высших.
      • Повторное использование высших уровней невозможно без использования нижних.
    • DIP - проектирование многоуровневых систем (2) DIP позволяет решить описанные проблемы.
    • The Interface Segregation Principle (ISP)
      • Clients should not be forced to depend upon interfaces that they do not use.
      • Robert C. Martin, “The Interface Segregation Principle”
      • … to be continued …