Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Особенностью ООП является то.doc
Скачиваний:
30
Добавлен:
09.02.2015
Размер:
467.97 Кб
Скачать

Дополнительные средства и приемы разработки классов

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

Метаклассы

Дальнейшее развитие идеи реализации полиморфных объектов привело к появлению более высокого уровня абстракции - метаклассов. Метакласс- тип, значениями которого являются классы, как ссылки на типы. Переменные типа метакласса можно использовать вместо явного указания класса в соответствии с традиционными правилами косвенного доступа (рис.).

Реализация метаклассов базируется на использовании специальных таблиц, в которых хранится информация о классе: имя класса, имя класса-родителя, адреса методов и т.д. Поскольку эти таблицы используются во время выполнения программы, то они получили название RTTI (Run Time Type Information - «информация о типе времени выполнения»).

Эти же таблицы используются для реализации следующих операций:

  • операция проверки принадлежности объекта заданному классу или его потомкам;

  • операция уточнения класса объекта - отличается от операции явного переопределения типа тем, что перед переопределением проверяется, при­надлежит ли объект данному классу или его потомкам;

  • специальные операции определения, модификации или проверки свойств класса (как типа) - для реализации этих операций язык программирования должен предоставлять возможность описания полей и методов класса, обращение к которым возможно при отсутствии объектов класса.

Делегирование методов

Делегирование- способ заимствования методов у объектов других классов. Оно представляет собой альтернативу переопределению методов, используемому полиморфными объектами. В отличие от переопределения, делегирование позволяет определять различное поведение объектов, принадлежащих одному классу. Причем заимствование методов возможно как в пределах класса или иерархии классов, так и у объектов классов других иерархий.

Метод при этом вызывается косвенно, через указатель на него. Язык, в котором возможна реализация делегирования, должен обеспечивать возможность определения указателей на методы. Назначение или замена метода осуществляются присваиванием соответственного значения специальному указателю (рис.).

Следует различать статическое и динамическое делегирование. При статическом делегировании соответствующий указатель инициализируется в процессе компиляции программы и при выполнении программы не меняется. При динамическом делегировании значение указателю присваивается в процессе выполнения программы и может изменяться в зависимости от ситуации.

Статическое делегирование используется в двух случаях:

1) если требуемое поведение объекта уже описывалось для объектов другого класса - в этом случае делегирование позволяет избавиться от повторов в программе;

Примечание. В C++ в аналогичных случаях возможно использование множественного наследования, но оно может оказаться неэффективным, поскольку вместе с желаемым поведением придется унаследовать и все остальные элементы класса.

2) если класс объявлен с не полностью определенным поведением объектов (обычно так описываются некоторые библиотечные классы) и его поведение уточняется для конкретных экземпляров объектов.

Динамическое делегирование используется присоздании объектов с изменяемым поведением, когда конкретный вариант поведения определяется некоторыми внешними обстоятельствами.

Пример

Делегирование методов (класс «Фигура»). Рассматривается объектная декомпозиция простейшего текстового редактора, позволяющего рисовать окружности и квадраты с изменяющимися размером, цветом и положением. В результате этой декомпозиции получены три объекта: «Монитор», «Круг» и «Квадрат» (рис.).

Те же действия будет выполнять программа, при разработке которой использован объект с динамическим поведением. Объектная декомпозиция текстового редактора в этом случае будет включать два объекта – «Монитор» и «Фигура» (рис.).

Вариант 1.Класс Фигура описан без указания конкретного варианта выводимой фигуры.

Класс «Фигура 1»:

реализация

поля X, Y, R, Color

поле Адрес_метода_рисования

интерфейс

конструктор Создать (аХ, aY, aR, aColor, аАдрес_метода_рисования)

метод Изменитьцвет (aColor)

метод Изменить_размер (aR)

метод Изменить_местоположение (аХ, aY)

Конец описания.

Адрес конкретного метода рисования должен указываться при создании соответствующего объекта. Изменение этого адреса в процессе выполнения программы не предусмотрено. В данном случае используется статическое делегирование.

Вариант 2.Возможен другой вариант: класс Фигура содержит метод, который меняет используемую процедуру рисования.

Класс «Фигура 2»:

реализация

поля х, у, г, Color

поле Адрес_метода_рисования

интерфейс

конструктор Создать (ах, ay, ar, aColor, аАдрес_метода_рисования)

метод Изменить_цвет (aColor)

метод Изменить_размер (aR)

метод Изменить_местоположение (аХ, aY)

метод Изменить_тип_Фигуры (аАдрес_метода_рисования)

Конец описания.

Выполнив указанный метод для объектов данного класса, можно изменить процедуру рисования в процессе выполнения программы - динамическое делегирование.

Сами методы рисования могут быть определены как в том же, так и в другом классе, например:

Класс «Методы_рисования»:

интерфейс

метод Рисование_окружности(аХ, aY, aR, aColor)

метод Рисование_квадрата(аХ, aY, aR, aColor)

Конец описания.

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

Вариант 3.

Класс «Фигура З»:

реализация

поля X, Y, R, Color

поле Адрес_метода_рисования

метод Рисование_окружности

метод Рисование_квадрата

интерфейс

конструктор Создать (аХ, aY, aR, aColor, aFiguraJype)

метод Изменить_цвет (aColor)

метод Изменить_размер (aR)

метод Изменить_местоположение (аХ, aY)

метод Изменить_тип_фигуры (аТипфигуры)

Конец описания.

Параметр aFiguratype будет получать значение, определяющее подключаемый к объекту метод рисования.

При использовании делегирования необходимо следить, чтобы заголовок метода соответствовал типу указателя, используемого для хранения адреса метода.

Контейнерные классы

Контейнеры - это специальным образом организованные объекты, используемые для хранения объектов других классов. Для реализации контейнеров разрабатываются специальные контейнерные классы. Контейнерный класс обычно включает набор методов, позволяющих выполнять некоторые операции, как с отдельным объектом, так и с группой объектов. В виде контейнеров, как правило, реализуются сложные структуры данных (различные виды списков, динамических массивов и т.п.). Разработчик наследует от класса-элемента класс, в который добавляются нужные ему информационные поля, и получает требуемую структуру. При необходимости он может наследовать класс и от контейнерного класса, добавляя к нему свои методы (рис.).

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

Методы, реализующие поэлементную обработку, должны работать с полями данных, определенными в классах-потомках класса-элемента.

Поэлементную обработку реализуемой структуры можно реализовать двумя способами.

Первый способ - универсальный - заключается в использовании итераторов.

Второй - в определении специального метода, который содержит в списке параметров адрес процедуры обработки.

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

<очередной элемент>:=<первый элемент>

цикл-пока <очередной элемент> определен

<выполнить обработку>

<очередной элемент>:=<следующий элемент>

все-цикл.

Поэтому обычно он состоит из трех частей:

  • метод, позволяющий организовать обработку данных с начала (получение первого элемента структуры);

  • метод, организующий переход к следующему элементу;

  • метод, позволяющий проверить окончание данных.

Доступ к очередной порции данных при этом осуществляется через специальный указатель текущей порции данных (указатель на объект класса-элемента).

Пример

Контейнерный класс с итератором (класс «Список»).

Разработаем контейнерный класс «Список», реализующий линейный односвязный список из объектов класса «Элемент».

Класс «Элемент»:

поле Указатель_наследующий

Конец описания.

Класс «Список» включает три метода, составляющих итератор: метод Определить_первый, который должен возвращать указатель на первый элемент, метод Определить_следующий, который должен возвращать указатель на следующий элемент, и метод Конец_списка, который должен возвращать «да», если список исчерпан.

Класс «Список»

реализация

поля Указатель_напервый, Указатель_натекущий

интерфейс

метод Добавить_передпервым (аЭлемент)

метод Удалить_последний

метод Определить_первый

метод Определить_следующий

метод Конец_списка

Конец описания.

Тогда поэлементная обработка списка будет программироваться следующим образом:

Элемент:= Определить_первый

цикл-пока не Конец_списка

Обработать элемент, переопределив его тип

Элемент:= Определить_следующий

все-цикл

При использовании второго способа поэлементной обработки реализуемой структуры процедура обработки элемента передается в списке параметров. Такую процедуру можно определить, если известен тип обработки, например, процедура вывода значений информационных полей объекта. Процедура должна вызываться из метода для каждого элемента данных. В языках с жесткой типизацией данных тип процедуры должен описываться заранее, при этом часто невозможно предусмотреть, какие дополнительные параметры должны передаваться в процедуру. В таких случаях первый способ может оказаться предпочтительнее.

Пример

Контейнерный класс с процедурой обработки всех объектов (класс «Список»). В этом случае класс «Список» будет описываться следующим образом:

Класс «Список»

реализация

поля Указатель_напервый, Указатель_натекущий

интерфейс

метод Добавить_перед_первым (аЭлемент)

метод Удалить_последний

метод Выполнить_для_всех (аПроцедура_обработки)

Конец описания.

Соответственно, тип процедуры обработки должен быть описан заранее, с учетом того, что она должна получать через параметр адрес обрабатываемого элемента, например: Процедура_обработки (аЭлемент)

Использование полиморфных объектов при создании контейнеров позволяет создавать достаточно универсальные классы.

Параметризованные классы

Параметризованный класс (или шаблон) представляет собойопределение класса, в котором часть используемых типов компонент класса определяется через параметры. Таким образом, каждый шаблон определяет группу классов, которые, несмотря на различие типов, характеризуются одинаковым поведением. Переопределить тип в процессе выполнения программы нельзя: все операции конкретизации типа выполняются компилятором (точнее - препроцессором) (параметризованные классы реализованы в C++). Они часто используются для реализации контейнерных классов, причем обычно в качестве элемента выступает полиморфный объект, указатель на базовый класс которого и передается через параметр. Классы, полученные из такого шаблона, могут оперировать как с объектами базового класса, так и с объектами производных классов.

Пример

Шаблон классов (шаблон классов Список), Шаблон практически полностью повторяет описание класса, но везде, где должен указываться тип элемента, вместо него следует указать параметр (в нашем случае Тип_элемента):

Шаблон классов Список (Тип_элемента)

реализация

поле Указатель_напервый: Указатель на Тип_элемента

поле Указатель_натекущий: Указатель на Тип_элемента

интерфейс

метод Добавить_перед_первым (аЭлемент: Указатель на Тип_элемента)

метод Удалить_последний: Указатель на Тип_элемента

метод Определить_первый: Указатель на Тип_элемента

метод Определить_следующий: Указатель на Тип_элемента

метод Конец_списка

Конец описания.

При использовании шаблона указывается его имя и соответствующее значение параметра, например:

Список (Запись1)

или

Список (Запись2)

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