изменения несколькими независимыми потоками. Обратный пример: подкласс UnselectableDisplayItem может ограничить поведение своего суперкласса DisplayItem, запретив выделение объекта на экране. Часто подклассы делают и то, и другое.

Отношения одиночного наследования от суперкласса TelemetryData показаны на рис. 3-5. Стрелки обозначают отношения общего к частному. В частности, Cameradata - это разновидность класса SensorData, который в свою очередь является разновидностью класса TelemetryData. Такой же тип иерархии характерен для семантических сетей, которые часто используются специалистами по распознаванию образов и искусственному интеллекту для организации баз знаний [25]. В главе 4 мы покажем, что правильная организация иерархии абстракций - это вопрос логической классификации.  

Рис. 3-5. Одиночное наследование.

Можно ожидать, что для некоторых классов на рис. 3-5 будут созданы экземпляры, а для других - нет. Наиболее вероятно образование объектов самых специализированных классов ElectricalData и SpectrometerData (такие классы называют конкретными классами, или листьями иерархического дерева). Образование объектов из классов, занимающих промежуточное положение (SensorData или тем более TelemetryData), менее вероятно. Классы, экземпляры которых не создаются, называются абстрактными. Ожидается, что подклассы абстрактных классов доопределят их до жизнеспособной абстракции, наполняя класс содержанием. В языке Smalltalk разработчик может заставить подкласс переопределить метод, помещая в реализацию метода суперкласса вызов метода SubclassResponsibility. Если метод не переопределен, то при попытке выполнить его генерируется ошибка. Аналогично, в C++ существует возможность объявлять функции чисто виртуальными. Если они не переопределены, экземпляр такого класса невозможно создать.

Самый общий класс в иерархии классов называется базовым. В большинстве приложений базовых классов бывает несколько, и они отражают наиболее общие абстракции предметной области. На самом деле, особенно в C++, хорошо сделанная структура классов - это скорее лес из деревьев наследования, чем одна многоэтажная структура наследования с одним корнем. Но в некоторых языках программирования определен базовый класс самого верхнего уровня, который является единым суперклассом для всех остальных классов. В языке Smalltalk эту роль играет класс object.

У класса обычно бывает два вида клиентов [26]:

• экземпляры;

• подклассы.

Часто полезно иметь для них разные интерфейсы [27]. В частности, мы хотим показать только внешне видимое поведение для клиентов-экземпляров, но нам нужно открыть служебные функции и представления клиентам-подклассам. Этим объясняется наличие открытой, защищенной и закрытой частей описания класса в языке C++: разработчик может четко разделить, какие элементы класса доступны Для экземпляров, а какие для подклассов. В языке Smalltalk степень такого разделения меньше: данные видимы для подклассов, но не для экземпляров, а методы общедоступны (можно ввести закрытые методы, но язык не обеспечивает их защиту).

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

Наследование подразумевает, что подклассы повторяют структуры их суперклассов. В предыдущем примере экземпляры класса ElectricalData содержат элементы структуры суперкласса (id и timeStamp) и более специализированные элементы (fuelCell1Voltage, fuelCell2Voltage, fuelCell1Amperes, fuelCell2Amperes) [Некоторые языки объектно-ориентированного программирования, главным образом экспериментальные, позволяют подклассу сокращать структуру его суперкласса].

Поведение суперклассов также наследуется. Применительно к объектам класса ElectricalData можно использовать операции currentTime (унаследована от суперкласса), currentPower (определена в классе) и transmit (переопределена в подклассе). В большинстве языков допускается не только наследование методов суперкласса, но также добавление новых и переопределение существующих методов. В Smalltalk любой метод суперкласса можно переопределить в подклассе.

В C++ степень контроля за этим несколько выше. Функция, объявленная виртуальной (функция transmit в предыдущем примере), может быть в подклассе переопределена, а остальные (currentTime) - нет.

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

void TelemetryData::transmit() {

// передать id // передать timeStamp

};

В классе ElectricalData та же функция переопределена:

void ElectricalData::transmit() {

TelemetryData::transmit(); // передать напряжение // передать силу тока

};

Эта функция сначала вызывает одноименную функцию суперкласса с помощью ее явно квалифицированного имени TelemetryData::transmit(). Та передаст заголовок пакета (id и timeStamp), после чего в подклассе передаются его собственные данные.

Определим теперь экземпляры двух описанных выше классов:

TelemetryData telemetry; ElectricalData electrical(5.0, -5.0, 3.0, 7.0);

Теперь определим свободную процедуру:

void transmitFreshData (TelemetryData& d, const Time& t) {

if (d.currentTime() >= t)

d.transmit();

);

Что произойдет, если выполнить следующие два оператора?

transmitFreshData(telemetry, Time(60)); transmitFreshData(electrical, Time (120));

В первом операторе будет передан уже известный нам заголовок. Во втором будет передан он же, плюс четыре числа в формате с плавающей точкой, содержащие результаты измерений электрических параметров. Почему это так? Ведь функция transmitFreshData ничего не знает о классе объекта, она просто выполняет d.transmit()! Это был пример полиморфизма. Переменная d может обозначать объекты разных классов. У этих классов есть общий суперкласс и они, хотя и по разному, могут реагировать на одно и то же сообщение, одинаково понимая его смысл.

Карделли и Вегнер заметили, что 'традиционные типизированные языки типа Pascal основаны на той идее, что функции и процедуры, а следовательно, и операнды должны иметь определенный тип. Это свойство называется мономорфизмом, то есть каждая переменная и каждое значение относятся к одному определенному типу. В противоположность мономорфизму полиморфизм допускает отнесение значений и переменных к нескольким типам' [28]. Впервые идею полиморфизма ad hoc описал Страчи [29], имея в виду возможность переопределять смысл символов, таких, как '+', сообразно потребности. В современном программировании мы называем это перегрузкой. Например, в C++ можно определить несколько функций с одним и тем же именем, и они будут автоматически

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату