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

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

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

...

};

Неожиданно мы столкнулись с затруднением. Утверждается, что пингвины могут летать, что, как известно, неверно. В чем тут дело?

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

class Bird {

... //
функция fly не объявлена

};

class FlyingBird: public Bird {

public:

virtual void fly;

...

};

class Penguin: public Bird {

... // функция fly не объявлена

};

Данная иерархия гораздо точнее отражает реальность, чем первоначальная.

Но и теперь еще не все закончено с «птичьими делами», потому что для некоторых приложений может и не быть необходимости делать различие между летающими и нелетающими птицами. Так, если ваше приложение в основном имеет дело с клювами и крыльями и никак не отражает способность пернатых летать, вполне сойдет и исходная иерархия. Это наблюдение, сообственно, является лишь подтверждением того, что не существует идеального проекта, который подходил бы для всех видов программных систем. Выбор проекта зависит от того, что система должна делать – как сейчас, так и в будущем. Если ваше приложение никак не связано с полетами и не предполагается, что оно будет связано с ними в дальнейшем, то вполне можно не принимать во внимание различий между летающими и нелетающими птицами. На самом деле даже лучше не проводить таких различий, потому что его нет в мире, который вы пытаетесь моделировать. Существует другая школа, иначе относящаяся к рассматриваемой проблеме. Она предлагает переопределить для пингвинов функцию fly так, чтобы во время исполнения она возвращала ошибку:

void error(const std::string& msg); // определено в другом месте

class Penguin: public Bird {

public:

virtual void fly {error(“Попытка заставить пингвина летать!”);}

...

};

Важно понимать, что это здесь имеется в виду не совсем то, что вам могло показаться. Мы не говорим: «Пингвины не могут летать», а лишь сообщаем: «Пингвины могут летать, но с их стороны было бы ошибкой это делать».

В чем разница? Во времени обнаружения ошибки. Утверждение «пингвины не могут летать» может быть поддержано на уровне компилятора, а соответствие утверждения «попытка полета ошибочна для пингвинов» реальному положению дел может быть обнаружено во время выполнения программы.

Чтобы обозначить ограничение «пингвины не могут летать – и точка», следует убедиться, что для объектов Penguin функция fly не определена:

class Bird {

... // функция fly не объявлена

};

class Penguin: public Bird {

... // функция fly не объявлена

};

Если

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

Penguin p;

p.fly; // ошибка!

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

Возможно, вы решите, что вам недостает интуиции орнитолога, но вполне можете положиться на свои познания в элементарной геометрии, не так ли? Тогда ответьте на следующий простой вопрос: должен ли класс Square (квадрат) открыто наследовать классу Rectangle (прямоугольник)?

«Конечно! – скажете вы. – Каждый знает, что квадрат – это прямоугольник, а обратное утверждение в общем случае неверно». Что ж, правильно, по крайней мере, для школы. Но мы ведь решаем задачи посложнее школьных.

class Rectangle {

public:

virtual void setHeight(int newHeight);

virtual void setWidth(int newWidth);

virtual int height const; // возвращают текущие значения

virtual int width const;

...

};

void makeBigger(Rectangle& r) // функция увеличивает площадь r

{

int oldHeight = r.height;

r.setWidth(r.width + 10); // увеличить ширину r на 10

assert(r.height == oldHeight); // убедиться, что высота r

} // не изменилась

Ясно, что утверждение assert никогда не должно нарушаться. Функция make-Bigger изменяет только ширину r. Высота остается постоянной.

Теперь рассмотрим код, который посредством открытого наследования позволяет рассматривать квадрат как частный случай прямоугольника:

class Square: public Rectangle {…};

Square s;

...

assert(s.width == s.height); // должно быть справедливо для

// всех квадратов

makeBigger(s); // из-за наследования, s является

// Rectangle, поэтому мы можем

// увеличить его площадь

assert(s.width == s.height); // По-прежнему должно быть справедливо

// для всех квадратов

Как и в предыдущем примере, что второе утверждение также никогда не должно быть нарушено. По определению, ширина квадрата равна его высоте.

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

Стеллар. Трибут

Прокофьев Роман Юрьевич
2. Стеллар
Фантастика:
боевая фантастика
рпг
8.75
рейтинг книги
Стеллар. Трибут

Его огонь горит для меня. Том 2

Муратова Ульяна
2. Мир Карастели
Фантастика:
юмористическая фантастика
5.40
рейтинг книги
Его огонь горит для меня. Том 2

На границе империй. Том 9. Часть 4

INDIGO
17. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 9. Часть 4

Наследник

Кулаков Алексей Иванович
1. Рюрикова кровь
Фантастика:
научная фантастика
попаданцы
альтернативная история
8.69
рейтинг книги
Наследник

Совершенно несекретно

Иванов Дмитрий
15. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Совершенно несекретно

Ваше Сиятельство 2

Моури Эрли
2. Ваше Сиятельство
Фантастика:
фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Ваше Сиятельство 2

Прометей: каменный век II

Рави Ивар
2. Прометей
Фантастика:
альтернативная история
7.40
рейтинг книги
Прометей: каменный век II

Единственная для темного эльфа 3

Мазарин Ан
3. Мир Верея. Драконья невеста
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Единственная для темного эльфа 3

Жандарм

Семин Никита
1. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
4.11
рейтинг книги
Жандарм

Долгий путь домой

Русич Антон
Вселенная EVE Online
Фантастика:
космическая фантастика
попаданцы
6.20
рейтинг книги
Долгий путь домой

Прогрессор поневоле

Распопов Дмитрий Викторович
2. Фараон
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прогрессор поневоле

Наследие Маозари 6

Панежин Евгений
6. Наследие Маозари
Фантастика:
попаданцы
постапокалипсис
рпг
фэнтези
эпическая фантастика
5.00
рейтинг книги
Наследие Маозари 6

Я еще не барон

Дрейк Сириус
1. Дорогой барон!
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Я еще не барон

Лолита

Набоков Владимир Владимирович
Проза:
классическая проза
современная проза
8.05
рейтинг книги
Лолита