Чтение онлайн

на главную - закладки

Жанры

Идиомы и стили С++

Makhmutov Albert

Шрифт:

Тем не менее, функций в классах остается достаточно много. Очевидно, что они группируются по своему назначению. Практически всегда есть группы, отвечающие за:

1. конструирование и инициализацию;

2. уничтожение и деактивацию;

3. сохранение и загрузку;

4. отображение;

5. обработку сообщений (событий).

Тут можно провести такую аналогию: если каждую функцию представить в виде одного провода, то их можно объединить в стандартный разъем: LPT, RS-232 или иной, и этот разъем будет обладать новыми, высшими свойствами, какими по отдельности провода не обладают; объединяя функции в цельные функциональные наборы мы так же получаем нечто новое. Присвоим этим наборам название, потом займемся реализацией. Термин возьмем у Microsoft.

Необычно, нетривиально, метко, а главное, свежо: интерфейс. Элджер дает термин facet (грань), и suite (комплект, а не костюм). Где-то я еще видел термин sub-pointer, но этот термин применим только для одной реализации, но не отражает общей концепции. По счастью, именно об этой реализации мы и собираемся поговорить.

Итак, как же объединить функции-члены в наборы, опираясь на средства языка? Да просто: определить их в абстрактных базовых классах, а потом объединять их при помощи множественного наследования. Это вполне неплохая идея. Именно так создаются объекты на основе ATL: дается набор стандартных шаблонов для стандартных интерфейсов, потом объединяется при помощи множественного наследования. Указатели на интерфейсы Вы можете легко получать при помощи dynamic_cast‹T›, только на всякий случай обрабатывайте исключение (обратного преобразования так легко не сделать, к сожалению; вообще это проблема - преобразование базового класса в производный в случае множественного наследования; я собираюсь поговорить об этом позже, а в этом Шаге заклинаю Вас не использовать явного преобразования указателей, только dynamic_cast‹T› с перехватом исключения и проверкой на NULL). Но есть и недостатки, причем кроме чисто технических, на мой взгляд, есть еще один серьезный этап проектирования: множественное наследование в объектном анализе, применяется для реализации какого-то одного, редкого аспекта поведения, присущего разным несвязанным классам - "повадки" класса. Наследование от класса, склеенного из нескольких других, выведет проблему на новый уровень иерархии. Вывод - такая техника оправдана в листьях дерева классов, в самом низу иерархии.

Вариант номер два: использование нескольких умных указателей (далее я буду также называть smart– указателями или smart-pointer), каждый из которых предоставляет доступ к ограниченному, но функционально полному набору функций-членов (это называется полный минимальный интерфейс, то есть предоставляющий все необходимое, и ничего свыше совершенно необходимого). Вы уже знаете о них достаточно, и приводить подробный код будет теперь несколько несерьезно, разве что самые общие черты:

class Cоbject {

// функции набора 1

// функции набора 2

};

class Cfuncset1 {

private:

 Cobject* obj;

public:

// функции набора 1

};

class Cfuncset2 {

private:

 Cobject* obj;

public:

// функции набора 2

};

Обращаю внимание - использовать нужно именно простые умные указатели, или интерфейсные указатели из Шага 7, но не ведущие. Проблемы этого варианта очевидны, хотя бы чисто технические: преобразование интерфейсов и создание-удаление реального указываемого объекта. Smart– указатели тоже можно наследовать от абстрактных базовых классов, тогда Вы получите определенную жесткость своей конструкции. Сам указываемый объект может быть каким угодно. Склеили ли вы его через множественным наследование, или как единое целое он вообще не существует, а реализован как несколько жестко связанных объектов (бывает и такое) - клиентам это без разницы, если

они все равно имеют к нему доступ через стандартные интерфейсы.

Подробности разберем в следующих Шагах, а сейчас я призываю Вас вспомнить, как в COM/DCOM сделано сохранение объектов: Вы имеете IStorage– умный указатель на объект "файл-хранилище", IPersist– умный указатель на объект, подлежащий записи, затем натурально сцепляете их, передавая указатель на IStorage в IPersist, и теперь Ваш объект просто выливается в свое хранилище, как молоко в глиняный кувшин. Объект может иметь любое количество иных интерфейсов, но наличие стандартного IPersist позволяет легко и красиво выполнить стандартную операцию.

Последнее: читайте Гради Буча! Учтите, что с первого раза он никогда (вообще!) не доходит. Только на второй или третий, не меньше, и то, если будете перемежать его с UML. Если Ваш проект действительно серьезный, то без грамотной модели он не существует. Этель Лилиан Войнич никогда бы не дописала свой роман "Овод", если бы с самого начала не задалась моделью: класс ‹солдат› обязательно должен иметь функцию ‹застрелить› экземпляр класса ‹мятежник›, а класс ‹офицер› во-первых, наследует от класса ‹солдат›, во-вторых, может отсортировать набор ‹солдат› в порядке возрастания - построить их на расстрел, в третьих - может и сам пристрелить врага при необходимости. Сильно подозреваю, что в этой модели функция ‹застрелить› является для ‹мятежника› дружественной. Выбирайте друзей правильно!

Шаг 10 - Множественные интерфейсные указатели. Продолжение.

Humpty-Dumpty: "With a name of Your,

You might be any shape, almost!"

L. Carroll. Throw the looking glass.

Сейчас мы поговорим о реализации, но до начала позвольте мне вернуться немного назад и добавить, что есть еще один неплохой способ организации множества интерфейсов. Он выглядит слегка неуклюже, но при известной дисциплине вполне работает: это метод вложенных классов из MFC COM. В самых общих чертах - там применяется явное получение смещения родительского класса от вложенного. Желающие могут посмотреть в MSDN по ключевому слову METHOD_PROLOGUE.

В пределах данного Шага я использую термин "Интерфейс" в смысле "smart-указатель", а термин "объект" в смысле "сложный указываемый объект", несмотря на то, что меня тошнит от этих слов. Если у Вас есть более подходящие, пишите, буду счастлив. Код я снова не проверяю, здесь нет ничего такого сложного, важна лишь идея.

Итак, мы уже решили иметь к некоему сложному объекту набор стандартных интерфейсов, и реализовать их при помощи smart– указателей. Но сами по себе они не имеют никакой пользы, если мы не сможем получать интерфейсы и объект друг из друга; для этого надо заиметь специальные средства, ибо интерфейсные указатели есть самостоятельные объекты, и вообще могут изменять свой указываемый объект в течение своего существования, и даже изменять тип объекта, если угодно (этого не может даже BASIC).

Для получения интерфейса по объекту проще всего нарисовать конструктор, получающий в качестве аргумента объект:

// Это объект

CComplexObject {};

// Это интерфейс

CInterface {

private:

 CComplexObject* co; // Укаэатель на объект

public:

 CInterface (CComplexObject _co){}; // Это конструктор

};

Немного подумав, решаем перенести обязанности по порождению интерфейсов на объект. Конструктор интерфейса перекладываем в private, объявляем класс объекта дружественным классу интерфейса, в классе объекта перегружаем операторы преобразования (или русским языком говоря - рисуем операторы преобразования объекта к интерфейсу).

Поделиться:
Популярные книги

Единственная для невольника

Новикова Татьяна О.
Любовные романы:
любовно-фантастические романы
5.67
рейтинг книги
Единственная для невольника

Кодекс Охотника. Книга XXI

Винокуров Юрий
21. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XXI

Любовь по инструкции

Zzika Nata
Любовные романы:
любовно-фантастические романы
5.85
рейтинг книги
Любовь по инструкции

Хозяйка старой пасеки

Шнейдер Наталья
Фантастика:
попаданцы
фэнтези
7.50
рейтинг книги
Хозяйка старой пасеки

Самый богатый человек в Вавилоне

Клейсон Джордж
Документальная литература:
публицистика
9.29
рейтинг книги
Самый богатый человек в Вавилоне

Север и Юг. Великая сага. Компиляция. Книги 1-3

Джейкс Джон
Приключения:
исторические приключения
5.00
рейтинг книги
Север и Юг. Великая сага. Компиляция. Книги 1-3

Случайная жена для лорда Дракона

Волконская Оксана
Фантастика:
юмористическая фантастика
попаданцы
5.00
рейтинг книги
Случайная жена для лорда Дракона

Последнее желание

Сапковский Анджей
1. Ведьмак
Фантастика:
фэнтези
9.43
рейтинг книги
Последнее желание

Печать Пожирателя

Соломенный Илья
1. Пожиратель
Фантастика:
попаданцы
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Печать Пожирателя

Бастард

Майерс Александр
1. Династия
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бастард

Возвышение Меркурия. Книга 14

Кронос Александр
14. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 14

Так было

Микоян Анастас
Документальная литература:
биографии и мемуары
5.00
рейтинг книги
Так было

Седьмая жена короля

Шёпот Светлана
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Седьмая жена короля

Госпожа Доктор

Каплунова Александра
Фантастика:
попаданцы
фэнтези
5.00
рейтинг книги
Госпожа Доктор