Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Языки программирования. Концепции и принципы

.pdf
Скачиваний:
2
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Объектно/ориентированное программирование
431
определенные для дуг, всюду перекрывают виртуальные операции, определенные для окружностей, в том числе и в самих операциях для окружностей. Ради послед него огород и городился!
Имена непосредственных компонент объекта должны быть уникальными как
в объявлении объектного типа, так и во всех его обогащениях – коллизии имен обычных полей не допускаются, а повторное использование имен операций раз решено только при замене виртуальных операций при обогащении объектного типа. (Профили всех одноименных виртуальных операций должны совпадать! Сравните с Адой и примером в Обероне.)
Тем самым становится особенно важным давать атрибутам объекта имена, от
ражающие их содержательные роли не только в создаваемой программе, но по возможности и во всех мыслимых ее развитиях, – ведь эти имена становятся «клю чевыми параметрами» развития программы.
По умолчанию для операций применяется статическое связывание (связыва
ние при объявлении типа и, следовательно, уже фиксированное при трансляции определяющего этот тип модуля). Для
виртуальных операций применяется так
называемое отложенное (задержанное) связывание с их именами. Это связыва ние при объявлении обогащенного типа, не завершенное при трансляции опреде ляющего модуля для исходного типа и, следовательно, требующее завершения при трансляции или исполнении определяющего модуля для обогащенного типа, в Турбо Паскале выполняется посредством ТВО.
Заметим, что обычно атрибутыполя не относят к абстрактным атрибутам.
Однако классификация атрибутов по степени их настраиваемости (конкретные, виртуальные, поля) не только помогает в очередной раз почувствовать пользу единого взгляда на абстракциюконкретизацию в ЯП (в частности, выделение аб стракции связывания), но и предсказать появление в перспективе ЯП, где будут правила, настраиваемые как с точностью до типа, так и с точностью до экземпля ра, и даже с точностью до конкретного исполнения экземпляра. Мы приходим к полностью динамическому связыванию операций, аппарат для которого уже продемонстрирован примером в Обероне.
С другой стороны, фактически и сейчас применяется оптимизация представления объектов, состоящая в том, что в объектах, к которым нет обращений с виртуальны ми правилами, не помещается ссылка на ТВО. Нетрудно представить себе возмож ность привязки к объекту виртуальной операции не динамически через ТВО, а прямой ссылкой на тело правила, настроенного на конкретный экземпляр объек та. Такая настройка может быть оправдана многократным выигрышем в скорости за счет систематической специализации тела операции по всем правилам конкре тизирующего программирования.
7.5. Критерий Дейкстры
В свое время Дейкстра, размышляя о том, какие процедуры следует выделять спе циальными ключевыми словами, – рекурсивные или нерекурсивные, пришел к выводу, что выделять следует ИСКЛЮЧЕНИЯ из общего правила, достойные
432
оптимизации (то есть процедуры НЕрекурсивные). Другими словами, если про граммист не указал явно, что процедура нерекурсивная, а компилятор не сумел самостоятельно распознать ее нерекурсивность при любых допустимых значени ях параметров, то последний обязан считать ее (потенциально) рекурсивной и со ответственно транслировать (возможно, менее эффективно, чем в нерекурсивном случае).
При таком решении автора ЯП, с одной стороны, усилия программиста требу
ются лишь тогда, когда нужна оптимизация, причем эти усилия требуются на формирование некоторого ЗАПРЕТА (на использование определяемого про граммного объекта), имеющего целью экономию машинных ресурсов.
Когда же ЯП по умолчанию предполагает исключение из общего правила, ори ентированное на оптимизацию, а технологически наиболее оправданный общий случай трактует как вариант, требующий специальных указаний программиста, то это, во5первых, провоцирует ошибки, во5вторых, засоряет программу и, нако нец, в5третьих, отвлекает внимание программиста на проблемы оптимизации от существенно более важных проблем правильности и надежности программы. На зовем этот критерий выбора для автора ЯП критерием Дейкстры. Этот критерий становится все более актуальным по мере роста цены живого труда по сравнению с ценой машинных ресурсов.
К сожалению, авторы ЯП Турбо Паскаль 5.5 не учли критерия Дейкстры (или не стали им руководствоваться), когда решили выделять ключевым словом virtual виртуальные операции, вместо того чтобы считать содержательно виртуальными любые процедуры из объявлений объектного типа, про которые не сказано явно обратное (для чего можно использовать, например, ключевое слово own). До статочно взглянуть на объявления типов Points и Circle, которые пестрят словом virtual, чтобы усомниться в том, что авторы поступили удачно.
А если вспомнить, что программист, не написавший этого сакраментального слова, ограничивает развиваемость (а следовательно, и тиражируемость) своей программы, причем не только в угоду эффективности, но и по ошибке, которая может быть обнаружена через годы эксплуатации программы (когда потребуется, наконец, обогатить именно то ее свойство, которое оказалось зависимым от опе рации, не объявленной в свое время виртуальной, – кстати, тестировать свой5 ство развиваемости программы – особая проблема), становится совершенно яс ным, что такое авторское решение следует признать недальновидным. Самое неприятное – в том, что исправить его практически невозможно – работает прин цип консерватизма языковых ниш. Оцените глубину критерия Дейкстры!
Перспективы языков программирования
7.6. Объекты и классы в ЯП СимулаE67
Уместно вспомнить здесь о Симуле67 как первом объектноориентированном ЯП [36, 37]. Поразительно, сколь точно авторы этого классического ЯП угадали перспективы программирования. Нетрудно провести прямые аналогии только что рассмотренных понятий из самых современных ЯП и понятий Симулы67. Для краткости понятия последнего будем выделять приставкой «с».
Объектно/ориентированное программирование
Действительно, склассы – это типы объектов с квазидинамическим контро
лем (контроль по квалификации сссылок, то есть типу указателей). Объект – это скласс (возможно, со своим квазипараллельным исполнением, то есть соткреп ленный). Обогащение (наследование) – это определение сподкласса с дополни тельными атрибутами. При этом старые операции применимы к новым объектам и присваивания «старым» сссылкам новых объектов возможно (но не наобо рот) – это управляется квалификацией сссылок (как уже отмечено, аналог типо вого контроля, кстати, частично статического – динамика требуется, например, когда сссылка родительского класса присваивается сссылке на потомка, – такое может быть и корректным, если на самом деле родительская ссылка является ссылкой на потомка (имеет право)).
Суправление видимостью развито удивительно для классического ЯП, непос
редственно наследовавшего блочную идеологию Алгола60, и неплохо обслужи вает объектную ориентацию языка (хотя, конечно, не учитывает модульности, которой в эталонном языке нет).
Соперации – это активные атрибуты собъектов, и действуют они «в рамках
собъектов» (то есть сами доступны (из других объектов!) – только через ссылку на собъект). При этом аргументом операции может служить любая компонента использующего эту операцию контекста (в соответствии со спецификацией ее па раметров). Если бы еще запретить прямой доступ к «пассивным» атрибутам собъектов, получился бы чистый аппарат для реализации абстрактных типов данных (атрибутыоперации скласса – это и есть операции соответствующего аб страктного типа данных). Однако в реальной Симуле67 такие «абстрактные типы» не защищены от разрушения.
Виртуальные операции – это почти в точности свиртуальные операции. При
чем и авторы Симулы67 не учли критерия Дейкстры – свиртуальные операции нужно выделять словом virtual.
Сдистанционные идентификаторы аналогичны обычным выборкам по селек
торам. Интересно подчеркнуть, что чем шире область возможных значений ссы лочной переменной (в соответствии с ее квалификацией, то есть типом), тем меньше атрибутов с ее помощью можно указать – это естественное следствие иерархии (обогащения) объектов. Однако в Симуле67 можно снять запрет на об ращение к атрибутам подклассов из надкласса (из бедного к обогащенному, из родительского к потомку) за счет явной разрешающей «оперативной» квалифи кации «qua»:
Родитель qua потомок.атрибут_потомка
Такое может понадобиться, когда фрагмент программы «выпадает из иерар
хии» и его проще всего поместить в родительский класс (например, для организа ции в нем взаимодействия между объектамипотомками из разных классов).
Другими словами, если нельзя, но очень хочется, то можно, но при этом нужно
явно сообщить транслятору о сознательном нарушении запрета. В подобном стиле действуют и авторы других «строгих» ЯП. Например, в Аде можно обойти конт роль типов, применив «фиктивное» преобразование типов посредством специально для этой цели предназначенной родовой функции UNCHECKED_CONVERSION.
433
434
Вместе с тем Симула67 на практике не смогла конкурировать с ЯП, не содер жавшими столь перспективных идей, хотя и была вполне справедливо представ лена авторами как «универсальный язык программирования» [36]. Не говоря уж о том, что этот ЯП явно опередил свое время, – масса программистов оказалась просто не готова воспринимать его ценности. Повидимому, важнейшим его недо статком оказалась относительно низкая эффективность исполняемых программ.
Дело в том, что в Симуле67 почти все интерпретируется (это же характерно и для Смолтока), а не компилируется, как в Турбо Паскале 5.5. В частности, нет статического создания объектов – они создаются динамически генераторами, нет статического контроля объектных типов (то есть квалификации сссылок – в об щем случае она контролируется динамически). Для относительной неудачи Си мулы67 сыграло свою роль и полное отсутствие средств модуляризации (раз дельной компиляции) на уровне эталонного языка. В более современных реализациях они, конечно, имеются (в частности, на отечественных компьютерах БЭСМ6 и ЕС [37]). С этой точки зрения, ЯП Симула67 унаследовал важнейший недостаток своего непосредственного предшественника и подмножества – Алго ла60, проигравшего Фортрану прежде всего изза отсутствия модулей.
Кроме того, нет разделения спецификации и реализации. Как уже отмечалось, нет защиты от несанкционированного доступа – контролируется только квали фикация ссылок, которой программист всегда может управлять, зная структуру программы (а не знать ее не может, так как при отсутствии разделения специфи кации и реализации, а также (в общем случае) раздельной компиляции вся она нужна для его работы). С другой стороны, авторы [37] утверждают, что можно иметь атрибуты, доступные только через соответствующие процедуры. Остается неясным, каким способом (в приведенном ими объяснении примера в [37, стр. 33– 34] имеются противоречия).
Перспективы языков программирования
7.7. Перспективы, открываемые объектной ориентацией средств программирования
Хотя мы рассмотрели далеко не все заслуживающие внимания примеры совре менных ЯП (а также аспекты) объектной ориентации, накоплено достаточно ма териала для обсуждения открываемых ею перспектив. Среди таких ЯП нужно на звать, по крайней мере, Смолток (в особенности Смолток/5 286) и Си++, а среди аспектов – переход от явного вызова операций к обмену сообщениями между объектами.
Будем считать последнюю идею понятной без специальных пояснений – дос таточно представить себе, что каждый объект снабжен анализатором сообщений, вызывающим при необходимости соответствующую операцию этого объекта. Другими словами, каждый объект снабжен минитранслятором сообщений, уст роенным, например, по принципу синтаксического управления. Конечно, в общем
Объектно/ориентированное программирование
случае такой подход требует значительных затрат на анализ сообщений в период исполнения программы. Однако при определенных ограничениях на класс сооб щений возможна весьма глубокая оптимизация (в перспективе с учетом конкре тизирующего программирования). Во всяком случае, прямой вызов операций по статически известным именам – частный случай обмена сообщениями.
По убеждению автора, объектная ориентация знаменует и стимулирует прин
ципиально новый уровень развития средств программирования (ЯП, в частности) потому, что позволяет естественно сочетать практически все рассмотренные нами (и некоторые иные) перспективные тенденции, тем самым создавая почву и для следующего витка развития (полезно в этой связи обратить внимание, например, на идеологию ЯП Оккам2 с его асинхронными процессамиобъектами и канала ми для обмена сообщениями), а также на отечественный язык НУТ [38] с его изящ ным соединением объектноориентированного, реляционного и концептуального программирования. Рассмотрим коротко представляющиеся наиболее интерес ными проблемы вместе с идеями их решений в рамках объектноориентированно го программирования. Для краткости его понятия и решения будем предварять префиксом «о».
Проблема управления
Основной оответ – децентрализация управления. Вместо представления о еди
ном исполнителе, выполняющем единую программу, создаваемую единым во мно гих лицах «богом»программистом, предлагается мыслить в терминах коллектива (коллективов) взаимодействующих объектовисполнителей, «живущих» в значи тельной степени самостоятельно (с точностью до овзаимодействия) в соответ ствии со своими собственными правилами поведения и «осоциальными» ролями. Создание, то есть программирование, такого ообщества также следует представ лять как довольно демократическую скорее историю, чем процедуру, существенно использующую развиваемость оиндивидов (как типов, так и объектов), а также относительно локальные договоренности о конкретных способах взаимодейст вия. Такую тенденцию можно обозначить метафорой «от автархии к анархии», в связи с чем рассматриваемый стиль программирования можно назвать «анархо ориентированным», если в соответствии с современными воззрениями снять с понятия «анархия» привкус априорного неприятия.
435
Проблема взаимодействия
Основной оответ (в перспективе) – относительно локальное взаимопонима
ние на основе взаимоприемлемого языка сообщений, совершенно не обязательно единого и понятного для всех. Более того, глубина понимания конкретных сооб щений участниками взаимодействия также может легко варьироваться в зависи мости от их роли в решении совместных задач.
Проблема ресурсов
Основной оответ – разнообразие как самих ресурсов, так и способов их созда
ния, предоставления и изъятия по соответствующим операциямзапросамсооб щениям соответствующими объектами. В принципе, в эту схему укладываются любые мыслимые варианты и их оптимизации.
436
Проблема развития
Основной оответ – идеал наследуемости. Однако в общей перспективе его следует дополнить динамизмом (вплоть до построения обогащенных объектов при работе других объектов), сближением понятия модуля с понятием объекта (объектного типа, класса), а также наследуемостью в языке обмена сообщениями.
Проблема защиты
И здесь основной оответ – идеал наследуемости, дополненный динамизмом при контроле корректности сообщений, а также в общем случае принципиальной невозможностью разрушить объект, если соответствующий приказсообщение не входит в согласованный язык сообщений.
Проблема классификации (типизации)
Основная метафора оответа – тип = язык. Эту метафору можно раскрыть и так, что типизация охватывает любые языковые конструкты (данные, операции, их сочетания – это путь к универсальному конструктиву типа [28]), и так, что средства описания типа тесно переплетаются со средствами определения полного языка (ведь при определении типа объектов нужно определять и воспринимае мый ими язык сообщений). Одно из «экстремистских», но не лишенных смысла толкований – к одному типу относятся объекты, «понимающие» определенный язык (или подъязык), обеспечивающий взаимодействие. [Так недалеко и до объектной нации.]
Проблема модульности
Общий оответ – модуль = объект (объектный тип). Существующие различия между этими понятиями связаны с особой ролью трансляции в жизненном цикле программы. Необходимость анализа и интерпретации сообщений в качестве ас пектов функционирования объектов превращает трансляцию в одну из рядовых операций и тем самым сближает логическую структуру программы с ее физиче ской структурой.
Перспективы языков программирования
Проблема свободы
Проблема свободы и ответственности естественно возникает перед каждой творческой личностью, в том числе (и в весьма острой форме) – перед программи стом. Поскольку объектная ориентация – специфический стиль программистско го мышления, а также определенная совокупность средств программирования, предоставляемая ЯП, интересно понять, какие ответы возможны в ее рамках.
Известно, что свобода хороша до тех пор, пока она не ограничивает свободу индивида, претендующего на тот же уровень свободы. С этой точки зрения основ ной оответ – единственным источником любых ограничений на свободу по
ведения объекта служит требование взаимопонимания (корректного обмена сообщениями) со всеми, кто ему самому необходим (для выполнения осоци альной роли).
Частным случаем такого требования служат и ресурсные ограничения, по скольку в общем случае ресурсы по требованию объекта предоставляются ему другими объектами. Другой частный случай – описание характера обмена сооб
Объектно/ориентированное программирование
щениямиоперациями в спецификации и полная свобода реализации при условии воплощения требований спецификации.
Таким образом, объектная ориентация действительно в максимальной степени
способствует свободному сочетанию самых разнообразных подходов к програм мированию отдельных объектов (объектных типов), требуя в общем случае лишь относительно локальных соглашений о необходимом «взаимопонимании» пере даваемых сообщений.
Проблема ответственности
Основной оответ – полная защита от несанкционированного (языком сооб
щений) вмешательства в поведение объекта, в результате чего его создатель по
лучает возможность полностью отвечать за корректность его поведения. Другими словами, никто не может заставить объект сделать то ( или сделать с ним то), чего объект не «понимает» и (или) не «контролирует» (ведь любое сообщение в общем случае анализируется самим объектом).
437
7.8. Свойства объектной ориентации
Новый уровень абстракции
Объектная ориентация ведет за счет нового уровня абстракции к обновлению фундаментальных концепций ЯП – управления, развития, защиты, классифика ции, модульности, динамизма (высокоразвитой типизацией), параллелизма (на следуемостью), спецификации, реализации, жизненного цикла программы (рас слоенным программированием и др.) – и в целом к сближению проблематики ЯП с проблематикой представления знаний и искусственного интеллекта.
Например, очевидно сходство используемого в объектноориентированном
подходе понятия объекта с понятием фрейма – одним из основных в современном представлении знаний. Понятие объекта можно считать одним из воплощений (своего рода конкретизацией) понятия фрейма. Стереотипная ситуация – это объектный тип, слоты – это поля и (или) операции (правила поведения), играю щие вполне определенную роль в рассматриваемой стереотипной ситуации, кон кретная ситуация – это экземпляр объекта.
Интеграция понятий и средств информатики
Объектноориентированное программирование знаменует очередной этап
сближения (интеграции) понятий и средств информатики, характерный для нее в последние годы и проявляющийся не только в названных областях, но и в созда нии интегрированных сред (вспомните о назначении ЯП Оберон), в сближении ЯП и СУБД (экспортное окно – аналог концептуальной схемы в СУБД, реляци онный ЯП близок к языку запросов реляционной БД), ЯП и языков логического программирования (вспомните о родстве Рефала с Прологом) и др.
Целостность ЯП
Наш анализ в очередной раз демонстрирует, что ЯП – целостная система. За
тронув лишь одно его свойство – развиваемость, мы на основе принципа концеп
438
туальной целостности «вытащили» новый взгляд почти на все аспекты ЯП. Если бы не уже отмеченный естественный консерватизм языковых ниш, ЯП уже могли бы стать совершенно иными. Искусство авторов новейших ЯП «объектной» ориен тации проявилось, в частности, в том, что такие ЯП, как Оберон, Турбо Паскаль
5.5 или Си++, оказались внешне очень похожими на своих более традиционных предшественников и вместе с тем во всех отношениях плавно вводящими пользо вателей в круг совершенно новых идей (в отличие от Симулы67 и тем более Смолтока, где к резким падением эффективности программ). С этим, возможно, в основном и свя зан их меньший успех у пользователей, хотя немалую роль сыграла и неготов ность программистской общественности к новой системе ценностей в программи ровании, провозглашающей самым дорогим ресурсом труд квалифицированного человека, а не, например, время работы или память компьютера.
тому же за эти весьма прогрессивные идеи нужно было платить
Перспективы языков программирования
7.9. Критерий фундаментальности языковых концепций
Судьба объектной ориентации (от неприятия ее при появлении в Симуле67 до современного бума) на весьма нетривиальном примере подтверждает один из ос новных тезисов нашей книги: почти все фундаментальные концепции про
граммирования (и современных ЯП) можно объяснить, не привлекая реали заторской позиции.
Другими словами, если необходимо привлекать реализаторскую позицию, то концепция не фундаментальна. Это, конечно, не умаляет исключительной значи мости применения наилучших алгоритмов и учета всех возможностей среды для коммерческого успеха программы.
Действительно, никакая проблема реализации не мешала еще двадцать лет на зад изготовить систему, по объектноориентированным возможностям сопоста вимую с Турбо Паскалем 5.5 (то есть включить их, а также соответствующие мо дульные средства, еще в первые версии Паскаля). Но само программирование
должно было созреть до понимания фундаментальной значимости удовлетворе5 ния потребности в развиваемости.
Заключительные замечания
8.1. Реализаторская позиция ........ 440
8.2. Классификация языков
программирования........................ 448
8.3. Тенденции развития ЯП .......... 451
Глава 8
440
Перспективы языков программирования
8.1. Реализаторская позиция
В самом начале книги (стр. 29) мы выделили пять позиций, с которых намерева лись рассмотреть ЯП. До сих пор реализаторской позиции уделялось мало внима ния. Настало время и нам несколько подробнее поговорить о реализации ЯП.
Безусловно, возможности и требования реализации оказывают существенное влияние на свойства ЯП. Долгое время это влияние считалось (а в значительной степени и было) определяющим. С ростом возможностей аппаратуры и методов трансляции оно ослабевает, уступая технологической позиции. Как уже сказано, основной методический тезис книги состоит в том, что подавляющее большинство свойств современных ЯП можно достаточно убедительно объяснить, не прибегая к реализаторской позиции. С другой стороны, о реализации ЯП написано много полезных книг (с точки зре ния общих потребностей программистов, вполне достаточно книги [39]). Поэтому постараемся уделить внимание лишь тем аспектам реализаторской позиции, кото рые в доступной литературе освещены недостаточно.
Напомним роль реализатора во взаимодействии с представителями остальных выделенных нами позиций. Реализатор призван обеспечить эксплуатацию ЯП на всех технологических этапах, опираясь на замысел автора.
Такое понимание роли реализатора (и реализации) ЯП не стало, к сожалению, общепринятым. Иногда еще приходится бороться с устаревшей точкой зрения, что задача реализатора – обеспечить ЯП исполнителем (языковым процессором, транслятором), и только. Именно такая узкая «реализаторская позиция» (имею щая глубокие корни) – одна из причин положения, при котором мы вынуждены пользоваться ненадежными трансляторами, колдовать над невразумительными диагностическими сообщениями, страдать от произвольных изменений ЯП, от сутствия сервиса, помогающего создавать и сопровождать программы, низкого уровня учебников, отсутствия методических материалов и т. п.
Нам не удастся рассмотреть задачу реализатора во всей ее полноте достаточно подробно. Поэтому поступим так же, как в случае технологической позиции. Как вы помните, мы кратко рассмотрели жизненный цикл изделия в целом, а затем выделили только проектирование как представительный этап этого цикла. Ана логичным образом дадим общее представление о задаче реализации ЯП в целом, а затем выделим лишь один аспект реализации и займемся только им.
Итак, будем считать, что реализация в целом должна обеспечить эксплуата цию ЯП на всех этапах жизненного цикла комплексного программного продук та (ЖЦКПП). Рассмотрим три этапа (стадии) жизненного цикла – проектирова ние, эксплуатацию и сопровождение продукта. Их достаточно, чтобы выделить важнейшие компоненты реализации.
8.1.1. Компоненты реализации
Будем исходить из того, что авторское определение ЯП имеется (для базового языка индустриального программирования в настоящее время это обычно отрас
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]