Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Прочая околокомпьтерная литература » Основы объектно-ориентированного программирования - Бертран Мейер

Основы объектно-ориентированного программирования - Бертран Мейер

Читать онлайн Основы объектно-ориентированного программирования - Бертран Мейер

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 143 144 145 146 147 148 149 150 151 ... 188
Перейти на страницу:

По этим причинам решение (1), требующее, чтобы программисты сами задавали политику связывания, мешает эффективному применению ОО-метода.

Решение (2) - использовать статическое связывание в качестве предопределенного - еще хуже. Очень трудно подобрать доводы в его пользу с точки зрения проектирования языка. Как мы видели, выбор статического связывания всегда приводит к ошибкам, если его семантика отличается от динамического. Поэтому не может быть никаких причин для его выбора в качестве предопределенного.

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

Даже в языке, заставляющем программиста отвечать за выбор политики связывания (такое решение принято в C), предопределенное значение должно быть противоположным. Вместо того, чтобы требовать объявлять динамически связываемые функции виртуальными (virtual), язык должен был бы использовать динамическое связывание по умолчанию и разрешить программистам выделять словом static (или каким-нибудь другим) компоненты, для которых они хотели бы запросить оптимизацию, доверив им самим (в традиции C и С++) удостоверяться в том, что она допустима.

Это различие особенно важно для начинающих, которые, естественно, имеют тенденцию доверять значениям по умолчанию. Даже для языка, менее страшного, чем С++, нельзя предполагать, что кто-либо сразу справится со всеми деталями наследования. Ответственный подход к этому должен гарантировать корректную семантику для новичков (и вообще, для разработчиков, начинающих новый проект, которые "хотят чтобы прежде всего он был правильным, а уж затем быстрым"), а затем предоставить возможности оптимизации для тех, кому это требуется и кто хорошо разбирается в предмете.

Имея в виду широко распространенный интерес к "совместимости снизу - вверх", создание комитета для изменения политики связывания в С++, особенно пункта (2), будет тяжелым делом, но стоит попытаться пролить свет на опасность нынешних соглашений.

Прискорбно, но подход С++ влияет и на другие языки, например, политика динамического связывания в языке Borland Delphi, продолжающем прежние расширения Паскаля, по сути, та же, что и в С++. Отметим все же, что вышедший из недр С++ язык Java в качестве базового использует динамическое связывание.

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

Парадокс этого совета в том, что он возвращает нас назад к ситуации, в которой все вызовы реализуются через динамическое связывание и требуют несколько большего времени выполнения. Иными словами, соглашения (1) и (2) языка С++, предназначенные для улучшения эффективности, в конце концов, если следовать правилу: "корректность прежде всего", срабатывают против этого!

Неудивительно, что эксперты по С++ не советуют использовать "чересчур много" объектной ориентированности. Уолтер Брайт (Walter Bright), автор одного из самых популярных компиляторов С++, пишет в [Bright 1995]:

Хорошо известно, что чем больше С++ [механизмов] вы используете в некотором классе, тем медленнее его код. К счастью, есть несколько вещей, позволяющих склонить чашу весов в вашу пользу. Во-первых, не используйте без большой необходимости виртуальные функции [т. е. динамическое связывание], виртуальные базовые классы [отложенные классы], деструкторы и т.п. Другой источник разбухания - это множественное наследование [...]. Если у вас сложная иерархия классов с одной или двумя виртуальными функциями, то попробуйте устранить виртуальный аспект и, быть может, сделать то же самое, используя проверки и ветвления.

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

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

Ключевые концепции

[x]. С помощью наследования можно определять новые классы как расширение, специализацию и комбинацию ранее определенных классов.

[x]. Класс, наследующий другому классу, называется его наследником, а исходный класс - его родителем. Распространенные на произвольное число уровней (включая ноль) эти понятия становятся понятиями потомка и предка.

[x]. Наследование является ключевым методом как для повторного использования, так и для расширяемости.

[x]. Плодотворное применение наследования требует переопределения (предоставления классу возможности переписать реализацию некоторых компонентов его собственного предка), полиморфизма (возможности связывать ссылку во время выполнения с экземплярами разных классов), динамического связывания (динамического выбора подходящего варианта переопределенного компонента), совместности типов (требования, чтобы всякая сущность могла присоединяться только к экземплярам типов-наследников).

[x]. С точки зрения модулей наследник расширяет набор служб, предоставляемых его родителями. В частности, это полезно для повторно использования.

[x]. С точки зрения типов отношение между наследником и его родителем - это отношение "является". Оно полезно как для повторного использования, так и для расширяемости.

[x]. Функцию без аргументов можно переопределить как атрибут, но не наоборот.

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

[x]. Для типизированных языков динамическое связывание можно реализовать с малыми накладными расходами. Связанные с ним оптимизации, в частности, применяемое компилятором статическое связывание и подстановка кода, помогают ОО-программам достичь или превзойти эффективность выполнения традиционных программ.

[x]. Отложенные классы содержат один или более отложенный (не реализованный) компонент. Они описывают частичные реализации абстрактных типов данных.

[x]. Способность эффективных подпрограмм вызывать отложенные позволяет примирить с помощью "классов поведения" повторное использование с расширяемостью.

[x]. Отложенные классы являются основным средством, используемым ОО-методами на стадиях анализа и проектирования.

[x]. Утверждения, применяемые к отложенным компонентам, позволяют точно специфицировать отложенные классы.

[x]. Если семантики динамического и статического связывания различны, то всегда нужно выбирать динамическое связывание. Если же они действуют одинаково, то статическое связывание следует рассматривать как оптимизацию, которую лучше возложить на компилятор. Компилятор может проверить и безопасно применить как эту оптимизацию, так и оптимизацию, связанную с подстановкой кода подпрограммы в точках вызова.

Библиографические замечания

Понятия (единичного) наследования и динамического связывания были введены в языке Симула 67, на который можно найти ссылки в лекции 17 курса "Основы объектно-ориентированного проектирования". Отложенные процедуры - это тоже изобретение Симулы (под другим именем (виртуальные процедуры) и при других соглашениях).

1 ... 143 144 145 146 147 148 149 150 151 ... 188
Перейти на страницу:
На этой странице вы можете бесплатно скачать Основы объектно-ориентированного программирования - Бертран Мейер торрент бесплатно.
Комментарии