Объектно-ориентированное проектирование с примерами

       

Тактические вопросы


В соответствии с законом разработки программ Коггинса "прагматизм всегда должен быть предпочтительней элегантности, ведь Природу все равно ничем не удивить". Следствие: проектирование никогда не будет полностью независимым от языка реализации проекта. Особенности языка неизбежно наложат отпечаток на те или иные архитектурные решения, и их игнорирование может привести к тому, что нам придется работать в дальнейшем с абстракциями, не в полной мере учитывающими преимущества и недостатки конкретного языка реализации.

Как было отмечено в главе 3, объектно-ориентированные языки предоставляют три основных механизма упорядочения большего числа классов: наследование, агрегацию и параметризацию. Наследование является наиболее популярным свойством объектно-ориентированной технологии, однако далеко не единственным принципом структурирования. Как мы увидим, сочетание параметризации с наследованием и агрегацией помогает создать достаточно мощную и в то же время компактную архитектуру.

Рассмотрим усеченное описание предметно-зависимого класса очереди в C++:

class NetworkEvent... // сетевое событие

class EventQueue { // очередь событий
public: EventQueue();
virtual ~EventQueue();
virtual void clear(); // очистить
virtual void add(const NetworkEvent&); // добавить
virtual void pop(); // продвинуть
virtual const NetworkEvent& front() const; // первый элемент

...
};

Перед нами абстракция, олицетворяющая очередь событий: структура, в которую мы можем добавлять новые элементы в конец очереди и удалять элементы из начала очереди. C++ позволяет скрыть внутренние детали реализации класса очереди за его внешним интерфейсом (операциями clear, add, pop и front ).

Нам могут потребоваться также некоторые другие варианты очереди, например, приоритетная очередь, где события выстраиваются в соответствии с их срочностью. Разумно воспользоваться результатами уже проделанной работы и организовать новый класс на базе ранее определенного:

class PriorityEventQueue : public EventQueue {


public: PriorityEventQueue();
virtual ~PriorityEventQueue();
virtual void add(const NetworkEvent&);

...
};


Виртуальность функций (например функции add) поощряет переопределение операций в подклассах.



Комбинация наследования с параметризованными классами позволяет создавать еще более общие абстракции. Семантика класса очереди не зависит от того, что в ней: волки или овцы. Используя классы-шаблоны, можно переопределить наш базовый класс следующим образом:

template<class Item>

class Queue {
public: Queue();
virtual ~Queue();
virtual void clear();
virtual void add(const Item&);
virtual void pop();
virtual const Item& front() const;

...
};


Это наиболее распространенный способ использования параметризованных классов: взять существующий конкретный класс, выделить в нем то, что не зависит от элементов, с которыми он оперирует, и сделать эти элементы аргументами шаблона.

Наследование и параметризация очень хорошо сочетаются. Наш подкласс PriorityQueue можно, например, обобщить следующим образом:

template<class Item>

class PriorityQueue : public Queue<Item> {
public: PriorityQueue();
virtual ~PriorityQueue();
virtual void add(const Item&);

...
};


Безопасность с точки зрения типов - ключевое преимущество данного подхода. Мы можем создать целый ряд различных классов конкретных очередей:

Queue<char> characterQueue;
typedef Queue<MetworkEvent> EventQueue;
typedef PriorityQueue<NetworkEvent> PriorityEventQueue;




Рис. 9-1. Наследование и параметризация.

При этом язык реализации не позволит нам присоединить событие к очереди символов, а вещественное число - к очереди событий.

Рис. 9-1 иллюстрирует отношения между параметризованным классом (Queue), его подклассом (PriorityQueue), примером этого подкласса (PriorityEventQueue) и одним из его экземпляров (mailQueue).

Этот пример подтверждает правильность одного из самых первых наших архитектурных решений: почти все классы нашей библиотеки должны быть параметризованными.Тогда будет выполнено и требование защищенности.


Содержание раздела