Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 66

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 60 61 62 63 64 65 < 66 > 67 68 69 70 71 72 .. 105 >> Следующая

Применительно к С++ это выглядит следующим образом: любая функция, требующая аргумента типа Person, или указателя на Person, или ссылки на Person, примет объект класса Student, или указатель на Student, или ссылку на Student:
void dance(const Persons p) ; void study(const Students s) Person p; Student s,-dance(p); dance(s);
study(s); study(p);
I1 Все люди могут танцевать.
Il Только студенты учатся.
// р - человек (Person).
Iis- студент (Student).
11 Нормально, р типа Person.
Il Нормально, s типа Student, а студент
// есть разновидность человека.
// Хорошо.
// Ошибка! р не студент.
Это верно только для открытого наследования. С++ будет вести себя так, как было описано выше, только если Student является производным классом от Person, который использует открытое наследование. Закрытое наследование означает нечто совершенно иное (см. правило 42), и никто, по-видимому, не знает, что должно означать защищенное наследование.
Идея тождества открытого наследования и понятия «есть разновидность» кажется достаточно очевидной, но не всегда все так просто. Иногда интуиция может ввести в заблуждение. Рассмотрим следующий пример: во-первых, пингвин - пти-; во-вторых, птицы умеют летать. Простодушно попытавшись выразить это на I-, получим:
class Bird { public :
ЕШИНШІІ*
Наследование и ООП
virtual void fly ();
Il Птины умеют летать.
};
class Penguin: public Bird {
Il Пингвины - птицы.
};
Неожиданно мы столкнулись с затруднением. Утверждается, что пингвин может летать, что, как известно, неверно. В чем тут дело?
В данном случае насподводит неточность разговорного языка. Говоря, что птицы умеют летать, мы в действительности утверждаем не то, что все птицы летают, а только то, что обычно они обладают такой способностью. Если бы мы выбирали формулировки поточнее, то вспомнили бы, что существует несколько видов птиц, которые не летают, и пришли бы к следующей иерархии, которая значительно лучше моделирует реальность:
class Bird {
...II Функция fly не объявлена.
};
classFlyingBird: public Bird { public:
virtual void fly();
};
class NonFlyingBird: public Bird { ...II Функция fly не объявлена.
};
class Penguin: public NonFlyingBird { ...II Функция fly не объявлена.
};
Данная иерархия намного больше, чем первоначальная, соответствует действительности.
Но и теперь еще не все закончено с «птичьими делами», поскольку для многих приложений вполне уместно было бы упоминание о том, что пингвины есть разновидность птиц. Так, если ваше приложение в основном имеет дело с клювами и крыльями и никак не отражает способность пернатых летать, вполне сойдет и исходная иерархия. Это наблюдение, собственно, является лишь подтверждением того, что не существует идеального проекта, который подходил бы для всех видов программных систем. Выбор проекта зависит от того, что должна делать система как в данный момент, так и в будущем. Если приложение никак не связано с полетами и не предполагается, что оно будет связано с ними в дальнейшем, сделать класс Penguin производным от класса Bird - вполне разумное решение. Оно предпочтительнее решения с разделением на летающих и не летающих птиц, поскольку в моделируемом вами мире эта характеристика отсутствует. Добавление лишних классов - свидетельство столь же плохого стиля проектирования, как и использование неправильных отношений наследования между классами.
Существует другая школа, иначе относящаяся к рассматриваемой проблеме. Она предлагает переопределить для пингвинов функцию fly так, чтобы в момент выполнения она возвращала ошибку.
Правило 35
void error(const strings msg); Il Определение в другом месте, class Penguin: public Bird { publіс:
virtual void fly() { error("Penguins can't fly!"); }
};
Интерпретируемые языки, например Smalltalk, часто используют такой подход, но важно осознавать, что при этом утверждается нечто совершенно отличное от того, что может показаться на первый взгляд. Вы не говорите: «пингвины не умеют летать». Вы говорите: «пингвины умеют летать,- но с их стороны было бы ошибкой пытаться делать это».
В чем различие? Во времени обнаружения ошибки. Утверждение «пингвин не умеет летать» может быть поддержано на уровне компилятора, а соответствие утверждения «Попытка полета ошибочна для пингвинов» реальному положению дел может быть обнаружено только в момент выполнения программы.
Чтобы обозначить ограничение «Пингвины не умеют летать», следует убедиться, что для объектов Penguin эта функция не определена:
class Bird {
};
class NonFlyingBird: public Bird {
};
class Penguin: public NonFlyingBird {
};
Il Функция fly не объявлена.
Il Функция fly не объявлена.
// Функция fly не объявлена.
Если вы попытаетесь заставить пингвина летать, компилятор сделает вам выговор за нарушение правил:
Penguin р; P-fly О;
// Ошибка!
Это сильно отличается от подхода, принятого в языке Smalltalk, где компилятор не скажет ни слова.
Философия С++ принципиально отличается от философии Smalltalk, и до тех пор, пока вы программируете на С++, вам лучше следовать его рецептам. Кроме того, обнаружение ошибок на этапе компиляции, а не в момент исполнения, имеет определенные технические преимущества (см. правило 46).
Предыдущая << 1 .. 60 61 62 63 64 65 < 66 > 67 68 69 70 71 72 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100