Slide 1

Slide 1 text

ОБЪЕКТНО- ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ Лекция № 1 / 10

Slide 2

Slide 2 text

ОТНОШЕНИЯ
 МЕЖДУ КЛАССАМИ • Наследование • Композиция • Агрегация • Ассоциация

Slide 3

Slide 3 text

namespace Expression { class Node { public: virtual double evaluate() const = 0; }; class Number: public Node { double number; public: virtual double evaluate() const { return number; } }; class UnaryOp: public Node { Node *arg; /* ... */ }; class BinaryOp: public Node { Node *left, *right; /* ... */ }; class Negate: public UnaryOp { /* ... */ }; class Add: public BinaryOp { /* ... */ }; } Наследование

Slide 4

Slide 4 text

Node Number UnaryOp BinaryOp Negate … Add …

Slide 5

Slide 5 text

ЧЕМ ХОРОШО НАСЛЕДОВАНИЕ? • Расширяемость. Полиморфизм. Имея указатель на объект базового класса, код может работать с любым его наследником. • Отсутствие дублирования. В идеале:
 
 Новый функционал = Старый функционал + Переопределение некоторых методов

Slide 6

Slide 6 text

ПРОБЛЕМНЫЕ ТОЧКИ НАСЛЕДОВАНИЯ • Нельзя отказаться от «наследства». Нельзя нарушать «контракт» базового класса. • Неудачный интерфейс базового класса делает переопределение неудобным или невозможным. • В большой и развесистой иерархии сложно разобраться. • Трудности с наследованием сразу от нескольких классов из одной иерархии.

Slide 7

Slide 7 text

class Text { public: virtual void draw() = 0; }; class BoldText: public Text { /* ... */ }; class ItalicText: public Text { /* ... */ }; class UnderlinedText: public Text { /* ... */ }; class StrikeText: public Text { /* ... */ }; class BorderedText: public Text { /* ... */ }; class BigText: public Text { /* ... */ }; class SmallText: public Text { /* ... */ }; Иллюстрация трудностей с наследованием

Slide 8

Slide 8 text

class BoldItalicUnderlinedBorderedBigText; // WTF?

Slide 9

Slide 9 text

class InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState extends State { // ... }; Это не шутка ☹

Slide 10

Slide 10 text

КОМПОЗИЦИЯ • Членами одного класса (хозяина) являются объекты других классов (рабы?). • Класс-хозяин управляет порождением и уничтожением этих объектов.

Slide 11

Slide 11 text

class GuiRectangle { public: // нарисовать прямоугольник void draw(); private: Rectangle rect; }; Уже знакомый пример

Slide 12

Slide 12 text

• Player: Visible, Solid, Movable. • Cloud: Movable, Visible, но не Solid. • Building: Solid, Visible, но не Movable. class Object { public: virtual void update() {} virtual void draw() {} virtual void collide(vector const &) {} }; class Visible : public Object { public: virtual void draw() { /* нарисовать модель на месте объекта */ }; private: Model *model; }; class Solid : public Object { public: virtual void collide (vector const &) { /* обработка столкновений */ } }; class Movable : public Object { public: virtual void update() { /* изменение положения */ }; }; Попытка наследования

Slide 13

Slide 13 text

class Object { VisibilityDelegate *_v; UpdateDelegate *_u; CollisionDelegate *_c; public: Object(VisibilityDelegate *v, UpdateDelegate *u, CollisionDelegate *c) : _v(v), _u(u), _c(c) {} void update() { _u->update(); } void draw() { _v->draw(); } void collide(vector const &objects) { _c->collide(objects); } }; class VisibilityDelegate { public: virtual void draw() = 0; }; class Invisible : public VisibilityDelegate { public: virtual void draw() {} }; class Visible: public VisibilityDelegate { public: virtual void draw() { /* отрисовка модели */ } }; Композиция

Slide 14

Slide 14 text

class CollisionDelegate { public: virtual void collide(vector const &) = 0; }; class Solid : public CollisionDelegate { public: virtual void collide(vector const &) { /* ... */ } }; class NotSolid : public CollisionDelegate { public: virtual void collide(vector const &) {} }; class UpdateDelegate { public: virtual void update() = 0; }; class Movable : public UpdateDelegate { public: virtual void update() { /* перемещаем объект */ }; }; class NotMovable : public UpdateDelegate { public: virtual void update() {} };

Slide 15

Slide 15 text

class Player : public Object { public: Player() : Object(new Visible(), new Movable(), new Solid()) {} // ... }; «Набираем» объект из делегатов

Slide 16

Slide 16 text

ПРЕИМУЩЕСТВА КОМПОЗИЦИИ • Одна большая иерархия заменяется на несколько меньших, не связанных между собой иерархий. Более простая система! • Не нужно тянуть всё из базового класса. Объект- делегат вообще можно создавать при необходимости. • Интерфейс класса-хозяина независим.

Slide 17

Slide 17 text

ПРОБЛЕМНЫЕ ТОЧКИ
 КОМПОЗИЦИИ • Объект-хозяин не входит в иерархию классов делегатов — полиморфизм не работает. • Нужно явно добавлять методы, вызывающие методы делегатов.

Slide 18

Slide 18 text

• Наследование: A является B (A is a B). • Композиция: A содержит B (A has a B). • Apple и Fruit. Яблоко является фруктом. Наследование уместно. • Employee и Person. Вроде бы сотрудник является человеком. Но в действительности сотрудник — это должность, замещаемая человеком (который может вообще потерять работу или совмещать несколько должностей). Лучше использовать композицию.

Slide 19

Slide 19 text

АГРЕГАЦИЯ • Аналог композиции, но без владения.

Slide 20

Slide 20 text

class Professor; class Department { // ... private: // Агрегация std::vector members; // ... }; class University { // ... private: // Композиция std::vector faculty; // ... void create_dept() { // ... faculty.push_back(Department(/* ... */)); faculty.push_back(Department(/* ... */)); // ... } }; // Если университет (University) уничтожается, то факультеты (Department) тоже // должны быть уничтожены. Но профессора (Professor) останутся! Кроме того, // один профессор может работать на нескольких факультетах, но факультет не может // принадлежать нескольким университетам.

Slide 21

Slide 21 text

vector vector В ЧЁМ РАЗНИЦА МЕЖДУ • Композиция • Хранятся сами объекты • Никакого полиморфизма • Агрегация • Объекты хранятся где- то ещё (где?) • Возможен полиморфизм по Object. и ?

Slide 22

Slide 22 text

АССОЦИАЦИЯ • Самая свободная форма связи между классами. • Один класс содержит указатель или ссылку на объект другого класса и вызывает его методы. • Порождение и уничтожение объекта происходит где-то вовне.

Slide 23

Slide 23 text

class Lamp { public: void on(); void off(); }; class ToggleButton { Lamp lamp; bool is_on; public: ToggleButton() : is_on(false) {} void toggle() { is_on = !is_on; if (is_on) lamp.on(); else lamp.off(); } }; Композиция (бессмысленная притом)

Slide 24

Slide 24 text

class Switchable { public: virtual void on() {} virtual void off() {} }; class Lamp: public Switchable { /* ... */ }; class ToggleButton { Switchable *object; bool is_on; public: ToggleButton(Switchable *o) : object(o), is_on(false) {} void toggle() { is_on = !is_on; if (is_on) object->on(); else object->off(); } }; Ассоциация

Slide 25

Slide 25 text

class MultiSwitch : public Switchable { vector objects; public: MultiSwitch(const vector &objs) : objects(objs) {} void on() { for (auto object: objects) object->on(); } void off() { for (auto object: objects) object->off(); } }; Вот что можно делать, когда нет жёстких связей

Slide 26

Slide 26 text

СИЛА СВЯЗЕЙ МЕЖДУ ОБЪЕКТАМИ Композиция Агрегация А с с о ц и а ц и я Обычно чем слабее, тем лучше!

Slide 27

Slide 27 text

ПРИНЦИП ОТКРЫТИЯ-ЗАКРЫТИЯ (OPEN-CLOSED PRINCIPLE) Программные объекты:
 (классы, модули, методы, …) Открыты для 
 расширения Закрыты
 для модификации но …

Slide 28

Slide 28 text

Client Server Теперь хочется так: Было: Client Cheap
 Server Quick
 Server Cool
 Server ?

Slide 29

Slide 29 text

Client Abstract
 Server Cheap
 Server Quick
 Server Cool
 Server Конечно же, выносим абстракцию в базовый класс!

Slide 30

Slide 30 text

Мега-задача
 Есть набор объектов (круги и квадраты),
 нужно уметь отрисовать этот набор

Slide 31

Slide 31 text

/* shape.h */ enum ShapeType { CIRCLE, SQUARE }; struct Shape { ShapeType type; }; /* circle.h */ struct Circle { ShapeType type; double radius; Point center; }; void drawCircle(struct Circle *); /* square.h */ struct Square { ShapeType type; double side; Point topLeft; }; void drawSquare(struct Square *); Процедурный style

Slide 32

Slide 32 text

/* drawallshapes.c */ typedef struct Shape *ShapePointer; void drawAllShapes(ShapePointer *list, int n) { int i; for (i = 0; i < n; ++i) { ShapePointer *s = list[i]; switch (s->type) { case SQUARE: drawSquare((struct Square *)s); break; case CIRCLE: drawCircle((struct Circle *)s); break; } } }

Slide 33

Slide 33 text

class Shape { public: virtual void draw() const = 0; }; class Square : public Shape { public: // ... virtual void draw() const; }; class Circle : public Shape { public: // ... virtual void draw() const; }; void drawAllShapes(Shape **list, int n) { for (int i = 0; i < n; ++i) list[n]->draw(); } OO style

Slide 34

Slide 34 text

switch (s->type) { case SQUARE: drawSquare((struct Square *)s); break; case CIRCLE: drawCircle((struct Circle *)s); break; } Закрытость для расширения Открытость для расширения class MyNewLovelyShape : public Shape { public: // ... virtual void draw() const; };

Slide 35

Slide 35 text

Усложнение 
 Сначала рисовать все
 окружности, а затем все квадраты

Slide 36

Slide 36 text

КАК ВООБЩЕ МОЖНО ВНОСИТЬ ИЗМЕНЕНИЯ? Способ первый Текущее состояние
 системы Фича + Новое состояние
 системы =

Slide 37

Slide 37 text

Состояние
 системы ещё фича + ещё фича + ещё фича + ещё фича + + =

Slide 38

Slide 38 text

ИНОЙ СПОСОБ Текущее состояние
 системы Фича и расщепляется на Абстракция Фича’ Дано: Шаг первый Фича

Slide 39

Slide 39 text

Текущее состояние
 системы + = Система с новой возможностью Шаг второй Шаг третий Система с новой возможностью Фича’ + = Система с реализованной новой возможностью Абстракция

Slide 40

Slide 40 text

Какая абстракция соответствует тому,
 чтобы рисовать сначала окружности, 
 а потом квадраты?

Slide 41

Slide 41 text

class Shape { public: virtual void draw() const = 0; virtual bool precedes(const Shape &another) const = 0; }; Задание порядка

Slide 42

Slide 42 text

КОНЕЦ ДЕСЯТОЙ ЛЕКЦИИ