Эйфель (язык программирования)
Эйфель (англ. Eiffel ) — объектно-ориентированный язык программирования с Паскаль-подобным синтаксисом, разработанный Бертраном Мейером. Программы на Эйфеле работают в MS Windows, OS/2 и Си, то есть являются своеобразным препроцессором. Далее этот код компилируется с помощью компилятора Си.
«Hello, world!» на Eiffel
Или развернуто:
author:
Примеры кода
Пример объявления класса на Eiffel:
где свойства — это общее название полей и методов. То есть атрибутов (attributes) и подпрограмм (routines) в терминологии Eiffel. Подпрограммы в подклассах могут замещать наследуемые только при совпадении их сигнатур. Ещё пример:
Ссылка определяется обычно:
complex_num: COMPLEX
Инициализация объекта (вызов конструктора):
Иногда используется замена слова create на такое:
!!complex_num.В языке Eiffel нет деструкторов — задействуется сборка мусора, когда на объект нет ссылок.
Язык Eiffel поддерживает множественное наследование (несколько разделов inherit ). Абстрактные классы определяются добавлением зарезервированного слова deferred («отложенный»):
deffered «Отложенные» свойства должны содержать определения в «неотложенных» потомках «отложенного» класса.
Самым характерным свойством языка Eiffel является встроенные утверждения для создания принудительного контракта между вызывающим оператором и вызываемым кодом подпрограмм (см. также протоязык Plakalkül).
Все связывания сообщений (здесь: «сообщение» — метафора ООП, проявляющая себя в вызове метода объекта) в языке Eiffel динамические. В первоначальной версии языка все объекты размещались в динамической памяти, и доступ к ним осуществлялся с помощью ссылок. Компиляторы с Eiffel были дороги и не так распространены как компиляторы C/C++, что, в своё время, и ограничило распространение этого языка.
Стиль оформления программ на Eiffel
Язык Eiffel спроектирован с максимальным уклоном в простоту конструкций языка. Так, в нем определена лишь одна конструкция для цикла.
При программировании на Eiffel хорошим тоном считается придерживаться определённых строгих правил оформления. Благодаря этому, исходники читаются так, как если бы это была документация. Дополнительным стимулом соблюдать правила, является строгость самих компиляторов к оформлению. Многие из них (в частности SmartEiffel) не позволяют неправильно оформлять исходные коды, находя многие отступления от стиля, отказываясь компилировать и выводя предупреждения.
Вот некоторые рекомендации по оформлению исходников:
- в отличие от C++, в Eiffel отрицательно относятся к сокращениям.
Вместо drvMngr принято писать driver_manager
- все классы пишутся прописными буквами
class LIST
- переменные пишутся строчными буквами
item_1 : INTEGER
- константы всегда начинаются с прописной
Universal_G : REAL is 9.81
- рекомендуется использовать немоноширинный шрифт
- служебные слова набираются жирным
- все идентификаторы — курсивом
- в начале каждого класса желательно вставлять информацию о нем: кто написал, когда, функциональность класса. Вся эта информация записывается в самом начале файла с классом.
indexing description:"Класс что-то должен делать" author: "Вася Пупкин" class КЛАСС --тело класса end
- после названия каждой процедуры на следующей строке желательно описывать результат её действия:
feature IP: INTEGER is -- возвращает IP адрес локальной машины do -- тело запроса end
- особенностью Eiffel является необязательность точки-запятой «;» в конце каждой строки.
См. также
- Сравнение возможностей Eiffel с другими языками см. в статье Сравнение языков программирования
Дополнительная информация
- «Object-Oriented Software Construction», Bertrand Meyer, Prentice Hall, 2nd edition 1997.
- «Eiffel: The Language». Bertrand Meyer
- «Invitation to Eiffel». From ISE.
- «An Overview of Eiffel». Richard Paige.
- «Object-Oriented Software Engineering with Eiffel» Jean-Marc Jezequel.
- http://www.elj.com/
- http://www.cetus-links.org/oo_eiffel.html
- http://www.eiffel.com/
- news://comp.lang.eiffel/
- Документация Эйфель на русском
Небольшое введение
Eiffel — это уникальный язык, появившийся в 1986 году. Не смотря на свои довольно продвинутые возможности он так и не привлек к себе значительного количества программистов, как говорят, не стал мейнстримом. Тем не менее, он не исчез и развивается до сих пор.
Ранее существовало несколько реализаций языка Eiffel. В настоящий момент, судя по всему, осталась только одна реализация — EiffelStudio компании Eiffel Software. Есть так же отдельная ветка эволюции Eiffel — SmartEiffel, но это уже практически новый язык, развивающийся независимо от своего родителя.
Существует стандарт ECMA 367 для языка Eiffel, но описанный там язык на данный момент пока никем не реализован. Eiffel Software декларирует намерение выпускать новые версии EiffelStudio два раза в год последовательно приближаясь к реализации Eiffel согласно ECMA 367.
Это состояние перехода Eiffel от одного варианта к другому значительно усложняет его изучение. Основной толмуд по Eiffel: Object Oriented Software Construction. 2nd Edition 1997 года (изданный на русском языке под названием Объектно-ориентированное конструирование программных систем в 2005 году) описывает состояние языка десятилетней давности. В состав EiffelStudio входит пара небольших руководств по языку, но они довольно поверхносны. Поэтому информацию об актуальном состоянии имеющейся в моем распоряжении версии языка приходится вытаскивать из разных источников, в том числе из ChangeLog-ов предшествующих релизов EiffelStudio.
Eiffel кажется более простым языком, чем C++. Однако, в отличии от некоторых других языков, Eiffel поддерживает всего лишь одну парадигму — объектно-ориентированную. Причем у разработчиков Eiffel имеется собственный взгляд на ООП. Поэтому Eiffel требует значительного времени на освоение (частично этому способствует и изрядный объем толмуда Object Oriented Software Construction). Из-за чего я не могу сказать что я хорошо освоил Eiffel. А посему данный обзор будет слишком поверхностным.
Основные черты языка
Прежде всего нужно сказать, что Eiffel позиционируется не как язык программирования сам по себе. Это часть некой одноименной методологии разработки программ. Но рассказ о методологии Eiffel явно выходит за рамки моих возможностей, поэтому придется органичится только рассмотрением языковых особенностей.
Сборка мусора. Eiffel поддерживает автоматическое управление памятью. Причем, в качестве положительной черты Eiffel декларируется то, что в многопоточной программе сборщики мусора для каждой из нитей работают независимо друг от друга. Это возможно, поскольку в Eiffel нет глобальных и статических переменных.
Только классы. Все типы в Eiffel представляются классами. Будь то целые числа, строки, массивы или хеш-таблицы — все это классы. Для эффективности в Eiffel есть разделение на ссылочные и расширенные (extended) классы. Главное различие между ними: если есть переменная ссылочного типа, то переменная содержит только ссылку на объект, а сам объект создается где-то там, в памяти. В случае же расширенного типа переменная содержит значение объекта. Такие классы как INTEGER_8 или CHARACTER_32 являются примерами расширенных классов.
Design By Contract. Основная отличительная черта языка. Для либого метода могут быть назначены специальные условия, которые проверяются при входе в метод (предусловия, precondition) и/или после выхода из метода (постусловия, postcondition). Так же для класса можно задавать инварианты (invariants) — условия, которые проверяются после создания экземпляра класса и при обращениях к любому из экпортированных (т.е. доступного клиентами класса) методов.
Утверждения. Специальная конструкция check, эквивалентная assert в C/C++/D, позволяющая вставлять дополнительные проверки в код.
Поддержка исключений. Но сильно отличающаяся от таковой в других языках.
Сокрытие информации. Eiffel предоставляет гибкие механизмы указания того, кто будет иметь возможность обращаться к методам и атрибутам класса.
Самодокументируемость. Стиль программирования на Eiffel располагает к написанию самодокументируемого кода. На это влияет соглашения об именовании, разделение методов по принципу команда/запрос, пред- и постусловия, инварианты. В дополнение к этому EiffelStudio позволяет строить из исходного текста документацию в различных форматах (что-то похожее на работу JavaDoc или Doxygen).
Наследование. Это вообще отдельная тема для разговора. Поддержка наследования в Eiffel является его такой же фундаментальной отличительной чертой, как и Design By Contract. Eiffel поддерживает множественное (именно от слова «много») наследование, по сравнению с которым множественное наследование в C++ — это только бледное подобие настоящего множественного наследования.
Обобщенное программирование. Это не шаблоны C++, скорее это похоже на механизм generic-ов в Java/C#. Тем не менее, контейнерные классы в Eiffel с самого начала были реализованы именно посредством обобщенного программирования.
Агенты. Механизм, очень похожий на указатели на функции в C/C++, делегаты в D. Позволяет передать метод объекта как параметр в какую-нибудь подпрограмму.
Однократные (once) методы. Уникальная по сравнению с C++/D/Java возможность, которая позволяет исполнять тело метода только один раз — при первом обращении к нему. Все остальные вызовы приводят к возврату ранее определенного значения.
Основные черты дизайна Eiffel программ
При программировании на Eiffel необходимо придерживаться ряда принципов (т.н. design principles) методологии Eiffel. Поскольку Eiffel не мультипарадигменный язык, он не позволяет сочетать в одной программе несколько стилей. Поэтому важно с самого начала придерживаться стиля Eiffel (который приверженцы Eiffel явно считают единственным правильным), в противном случае есть риск вообще не получить хорошего результата (проверено на собственном опыте).
Command/Query Separation Principle
Метод объекта не должен одновременно делать две вещи: изменять состояние объекта и возвращать какое-либо значение. Отсюда разделение на команды — методы, которые только изменяют состояние объекта, но ничего не возвращают, и запросы — методы, которые возвращают результат, но ничего не изменяют (т.е. не имеют побочных эффектов).
Из-за этого принципа программы на Eiffel выглядят как последовательность операций «сделал» и «спросил результат». Т.е. если в C++/D можно открыть файл и сразу получить результат операции:
File file; if( -1 != file.open( file_name, file_mode ) ) .
то в Eiffel эта операция будет выглядеть несколько иначе:
create file.make (file_name) file.open_read if file.is_open_read then . end
Information Hiding Principle
Здесь ничего нового: разработчик класса должен определить, какая часть класса будет свободно доступной (публичной), а какая будет скрытой. Отличие состоит в том, что в Eiffel можно гибко управлять видимостью компонентов класса. Например:
class A feature a is . end b is . end feature c is . end feature d is . end feature e is . end end
Методы a и b будут доступны всем, метод c — только классу B, метод d — только классам C и D, метод e — только классу A и его наследникам. Т.е. никаких public, protected, private или package — компонент либо виден кому-нибудь, либо нет.
При этом если класс A делает метод f видимым классу B, то говорят, что класс A экспортирует f классу B.
Uniform Access Principle
Компоненты (features в терминологии Eiffel) в классе могут быть двух видов: атрибуты (поля) и методы (подпрограммы). Принцип унифицированного доступа говорит о том, что по записи обращения к компоненту (например, такой: x.f ) не должно быть понятно, является ли компонент атрибутом или же методом.
Данный принцип начинает работать в полной мере, когда производный компонент переопределяет атрибут методом:
class A feature name: STRING -- name является атрибутом. end class B inherit A redefine name end feature name: STRING is . end -- name уже стало методом. end
В этом случае клиенты класса A, получившие через полиморфные ссылки ссылку на объект B не увидят разницы при работе с компонентом name.
Некоторые дополнения
В книге Object Oriented Software Construction декларируется несколько иной набор принципов:
Linguistic Modular Units модули должны соответствовать синтаксическим единицам языка; Self-Documentation разработчик модуля должен стремиться к тому, чтобы вся информация о модуле содержалась в самом модуле; Uniform Access все службы, предоставляемые модулем, должны быть доступны в унифицированной нотации, которая не подведет вне зависимости от реализации, использующей память или вычисления; Open-Closed модули должны иметь возможность быть как открытыми, так и закрытыми; Single Choice всякий раз, когда система программного обеспечения должна поддерживать множество альтернатив, их полный список должен быть известен только одному модулю системы.
Однако, это общие принципы проектирования программ в соответствии с методологией Eiffel. К исходным текстам программ гораздо большее отношение имеют три ранее перечисленных принципа.
Особенности синтаксиса языка Eiffel
Язык Eiffel не различает регистр символов, т.е. Class , class и CLASS — это один и тот же идентификатор. При этом в Eiffel существует весьма жесткое соглашение об именовании:
- названия классов должны использовать исключительно прописные буквы, т.е. используется UPPER_CASE нотация;
- названия методов должны использовать исключительно строчные буквы, т.е. используется lower_case нотация;
- константы и once-методы должны использовать в качестве первой прописную букву, а все остальные — строчные, т.е. First_upper_letter_only нотация.
Точки с запятыми в качестве разделителей выражений в Eiffel необязательны. Они должны использоваться только если несколько выражений пишутся на одной строке. Но, поскольку это в Eiffel не принято делать, точки с запятыми используется только как разделители в списках параметров методов.
Нужно отметить, что соглашения об оформлении кода в Eiffel весьма жесткие. Некоторые вещи, например, комментарии к отдельным секциям в описании классов или к методам, должны располагаться на определенных местах. Хотя синтаксисом языка это и не определено. Тем не менее, расположенные правильным образом комментарии используются EiffelStudio для построения документации и формирования дерева доступных классов/методов.
Что из себя представляет программа на Eiffel
Программа на Eiffel собирается из классов (class). Каждый класс находится в одном файле с расширением .e . Совокупность классов объединяется в кластер (cluster). Обычно все классы кластера находятся в одном подкаталоге, но это не обязательное требование — просто так удобнее.
Программа — система (system) — собирается из необходимых кластеров. При этом в программе нужно указать т.н. корневой класс (root class). Исполняющая система Eiffel создает экземпляр этого класса посредством специальной процедуры создания (root creation procedure). Всю свою работу программа должна выполнить внутри этой процедуры создания.
Вот так, к примеру, выглядит приложение «Hello, World» в Eiffel:
class APPLICATION create make feature -- Initialization make is -- Run application. do io.put_string ("Hello, world") io.put_new_line end end -- class APPLICATION
Здесь класс APPLICATION является корневым классом, а его процедура make — процедура создания корневого класса. Выход из make означает завершение работы программы.
Компилируется Eiffel программа в нативный код, но через промежуточную трансляцию в C с последующей компиляцией полученного C-кода. За счет этого достигается кроссплатформенность реализации Eiffel.
Процесс трансляции состоит из нескольких (порядка шести) стадий, на которых компилятор Eiffel выискивает все используемые Eiffel классы из всех использованных кластеров, анализирует и оптимизирует вызовы, генерирует C-код, запускает C компилятор и линкер.
Компиляция возможна в двух режимах — рабочем (workbench) и финальном (finalized). Примечательной чертой рабочего режима является использование т.н. технологии «тающего льда» (Melting Ice Technology) — при внесении изменений в исходный код не выполняется полная перекомпиляция, изменения транслируется в байт-код, который располагается рядом с ранее скомпилированным исполнимым файлом. Видимо, когда исполнимый файл запускается, он определяет наличие нового байт-кода и подгружает его в себя. За счет этого компиляция в рабочем режиме выполняется очень быстро, но полученный код работает заметно медленее. Компиляция же в финальном режиме занимает ощутимо больше времени (вероятно даже больше, чем C++), зато скорость работы сравнима со скоростью C++/D программ (хотя это сильно зависит от количества оставленных в финальном коде процедур проверки).
Последние версии EiffelStudio умеют транслировать Eiffel в .NET приложения. Документация утверждает, что в этом случае EiffelStudio генерирует сразу .NET код, без промежуточного C-представления.
Классы
В Eiffel программах нет ничего кроме классов и объектов этих классов. Обычное описание класса выглядит как перечисление всех компонентов (т.е. атрибутов и методов) в нескольких секциях feature :
class CLASS_NAME feature -- Header . feature -- Another Header . end
Если класс нуждается в конструкторах (т.н. creation procedures), то имена методов, которые выступают в качестве конструктором должны перечисляться в специальной секции create (в предыдущих версиях Eiffel она называлась creation ):
class SAMPLE create make, make_with_something feature -- Initialization make is . end make_with_something (arg: SOME_ARG_TYPE) is do . end . end
В Eiffel нет перегрузки методов по типам аргументов. Поэтому, если нужно выполнить какое-то действие с разным набором параметров или с параметрами разных типов, то нужно написать функцию с уникальным именем. Что видно на приведенных выше методах make и make_with_something.
Создание объекта в Eiffel выполняется специальной конструкцией create . Так, если создаваемый объект не имеет конструктора, его создание выглядит как:
local obj: SOME_OBJECT do create obj end
Если же объект имеет конструктор, то вызов конструктора указывается в конструкции create :
local obj: SOME_OBJECT_WITH_CONSTRUCTOR do create obj.make (some_arguments) end
Если нужно создать объект производного типа, а ссылку на него сохранить в объекте базового типа, то актуальное имя типа должно указываться в конструкции create :
local obj: BASE_TYPE do create obj.make end
Специальное ключевое слово Current является аналогом this в C++/D/Java.
Отложенные классы и отложенные методы
В Eiffel аналоги абстрактных классов и абстрактных методов называются отложенными (deferred).
deferred class STACK feature full: BOOLEAN -- Возвращает true, если стек полон. deferred end . end
Нельзя создавать объекты отложенных классов. Вызов отложенного метода приведет к возникновению ошибки.
Отложенные классы могут содержать инварианты, а отложенные методы — пред- и постусловия.
Замороженные классы и методы
В Eiffel cуществует понятия замороженных (frozen) классов и методов. Т.е. классов, от которых нельзя наследоваться и методов, которые нельзя переопределять.
Наследование
Наследование — это единственный способ распространения функциональности между классами (поскольку нет статических методов классов или свободных функций). Т.е. если какому-то классу потребовалась функциональность из другого класса, получить ее можно только наследованием. В связи с этим в Eiffel невозможно написать ничего более-менее серьезного без наследования. А, зачастую, и без множественного наследования.
Список базовых классов указывается в секции inherit описания класса:
class DEMO inherit FIRST_BASE SECOND_BASE . LAST_BASE feature . end
Переопределение методов
Если производный класс хочет переопределить какой-то метод базового класса, то он должен перечислить все переопределения в специальной секции redefine :
class DEMO inherit FIRST_BASE redefine hello, bye end feature hello is . end bye is . end end
Переименования методов
При множественном наследовании может оказаться так, что унаследованные методы желательно было бы переименовать. Сделать это можно с помощью секции rename :
class DEMO inherit FIRST_BASE rename hello as first_hello end feature hello is . end end
Объявление методов отложенными
Иногда бывает необходимо объявить унаследованный из базового типа метод отложенным. Сделать это можно с помощью секции undefine :
class DEMO inherit FIRST_BASE undefine hello end . end
Выбор методов при множественном наследовании
Может оказаться, что какой-то базовый класс будет унаследован несколько раз через разные пути наследования. Например, пусть FIRST_BASE и SECOND_BASE наследуются от SUPER_BASE . Тогда SUPER_BASE будет входить в состав DEMO дважды.
Соответственно, возникает вопрос — будут ли дублироваться компоненты SUPER_BASE в DEMO . Те компоненты, которые не были переопределены в FIRST_BASE и SECOND_BASE , войдут в DEMO в единственном экземпляре. Но, если какие-то компоненты были переопределены в одном из производных классов, то возникнет конфликт выбора версии компонента. Разрешить который можно с помощью секции select :
class DEMO inherit FIRST_BASE select hello end SECOND_BASE select bye end . end
В этом случае в качестве hello будет использоваться версия из FIRST_BASE , а в качестве bye — версия из SECOND_BASE .
Управление видимостью унаследованных компонентов
Eiffel позволяет в производном классе изменить описанные в базовом классе параметры экспорта компонентов. Например, если базовый класс экспортировал a классу A, b классу B, а c вообще не был никому доступен:
class BASE feature a is . end feature b is . end feature c is . end end
то производный класс посредством секции export может полностью изменить картину экспорта:
class DERIVED inherit BASE export a -- a уже никому не доступна. b -- b уже доступна и B, и C. c -- c уже доступна всем. end . end
При множественном наследовании может возникнуть необходимость скрыть от клиентов все методы, унаследованные от какого-то базового класса. В этом случае используется ключевое слово all :
class DEMO inherit FIRST_BASE SECOND_BASE export -- Никому не будут доступны унаследованные методы SECOND_BASE. all end . end
Обращение к версии из базового класса
Если нужно вызвать реализацию переопределенного метода из базового класса, то используется специальное ключевое слово Precursor :
class DEMO inherit FIRST_BASE redefine hello end feature hello is Precursor . end end
Приведения типов и попытка присваивания
В Eiffel нет специального синтаксиса для приведения типов. Если речь идет о том, чтобы преобразовать значение INTEGER_8 в NATURAL_8 , то нужно воспользоваться методом to_natural_8 класса INTEGER_8 .
Если речь идет о преобразовании типа ссылки (например, down-casting от базового типа к производному), то в Eiffel для этой цели предназначена специальная конструкция — попытка присваивания (assignment attempt):
do_something (b: BASE) is local d: DERIVED -- DERIVED является наследником BASE. do d ?= b if d /= Void then -- Точно известно, что b является экземпляром DERIVED. . else -- Точно известно, что b не является экземпляром DERIVED. end end
Циклы
В Eiffel есть только одна конструкция для организации циклов:
from [init_actions] until [invariant ] [variants ] loop end
Секция from может содержать набор инструкций для инициализации цикла, а может и быть пустой.
Секция until содержит условие выхода из цикла. Что вызывает серьезную ломку сознания при переходе на Eiffel из C/C++/Java/D.
Секция loop содержит тело цикла.
Все. Ничего другого для циклов в Eiffel нет.
Функции
Функции — это методы, которые возвращают результат. Отличие функций Eiffel от функций в других языках состоит в том, что
- Возвращаемое функцией значение нужно присвоить специальной встроенной переменной с именем Result .
- В Eiffel нет инструкции для принудительного выхода из функции, т.е. нет аналога C-шного return — функция всегда исполняется от начала до конца (если только она не прерывается исключением).
is_item_in_array (where: ARRAY [INTEGER]; what: INTEGER): BOOLEAN is -- Ищет элемент what в векторе where. local i: INTEGER found: BOOLEAN do found := false from i := where.lower until (i > where.upper) or (found) loop if what = where [i] then found := true end i := i + 1 end Result := found end
Этот пример можно переписать короче учитывая тот факт, что любая переменная или атрибут в Eiffel получает начальное значение. Для BOOLEAN — это false, для числовых типов — 0, для ссылочных типов — специальное значение Void (аналог null в Java/D). Это же правило распространяется и на переменную Result , поэтому можно обойтись без вспомогательной переменной found:
is_item_in_array (where: ARRAY [INTEGER]; what: INTEGER): BOOLEAN is -- Ищет элемент what в векторе where. local i: INTEGER do from i := where.lower until (i > where.upper) or (Result) loop if what = where [i] then Result := true end i := i + 1 end end
Если функция возвращает ссылку, то где-то в теле функции должна применяться конструкция create с использованием переменной Result :
string_to_binary (what: STRING): ARRAY [NATURAL_8] is -- Возвращает вектор ASCII кодов символов исходной строки. local i: INTEGER do create Result.make (1, what.count) from i := 1 until i > what.count loop Result.put (what.item_code (i), i) i := i + 1 end end
Design By Contract
Механизм Design By Contract позволяет задавать различные типы условий, проверяемых во время работы программы. Нарушение любого из условий приводит к выбрасыванию исключения. Т.е. нормальная работа прерывается и, если данное исключение должным образом не обрабатывается, приложение завершается.
Предусловия, постусловия и инварианты
Механизм Design By Contract базируется на трех основных конструкциях: предусловиях, постусловиях и инвариантах.
Предусловия записываются в специальной секции require реализации метода, постусловия — в секции ensure . А инварианты в специальной секции invariant описания класса. Общий формат таков:
[tag:] condition
где tag — это необязательный идентификатор, а condition — это условие. Подобных утверждений в пред-, постусловиях и инвариантах может быть сколько угодно.
Предусловия проверяются перед началом выполнения тела метода. Т.е. о выполнении предусловий должна позаботиться вызывающая сторона и метод не должен отрабатывать, если его предусловия не выполняются.
Постусловия проверяются после завершения тела метода. Т.е. о выполнении постусловий должна позаботиться реализация метода. В постусловиях можно использовать специальную конструкцию old , которая позволяет возвращает значения атрибутов, предшествовавшие вызову метода.
Инварианты проверяются перед и после завершения работы публичных методов.
Вот пример пред- и постусловий:
string_to_binary (what: STRING): ARRAY [NATURAL_8] is -- Возвращает вектор ASCII кодов символов исходной строки. require valid_argument: what /= Void local i: INTEGER do create Result.make (1, what.count) from i := 1 until i > what.count loop Result.put (what.item_code (i), i) i := i + 1 end ensure valid_result: Result /= Void result_has_same_size: what.count = Result.count end
Теги для условий (т.к. valid_argument и valid_result ), если заданы, включатся в описание ошибки, выдаваемое исполнительной системой Eiffel при обнаружении нарушения условия. Что довольно удобно при отладке.
Инварианты записываются в специальной секции invariant в описании класса:
class OPTIONS_PARSING_RESULT create make_ok, make_failed feature -- Initialization make_ok (parsed_options: OPTIONS) is -- Creation when parsing successful. do is_ok := true options := parsed_options end make_failed( failure_reason: STRING ) is -- Creation when parsing failed. require failure_reason /= Void do is_ok := false reason := failure_reason.twin end feature -- Access is_ok: BOOLEAN options: OPTIONS reason: STRING invariant options_iff_ok: is_ok implies (options /= Void) reason_iff_failure: (not is_ok) implies ((reason /= Void) and (options = Void)) end
Здесь инвариант указывает, что если атрибут is_ok равен true , то атрибут options не может быть пустой ссылкой. И напротив, если is_ok равен false , то options обязан быть пустой ссылкой, зато reason быть пустой ссылкой не может.
Предусловия и постусловия при наследовании
Пред- и постусловия задают публичный контракт для метода класса. Поэтому, будучи однажды определенными, они не могут быть просто так изменены в производных классах.
Предусловия задают требовани к клиенту класса. Поэтому предусловия нельзя ужесточать — ведь тогда клиент, отлично работавший с базовым классом, перестанет работать с производным. Можно только ослабить предусловия. Из-за этого, если производный класс переопределяет метод и определяет свои предусловия, то новые предусловия должны быть записаны в секции require else :
class BASE feature demo is require . do end end class DERIVED inherit BASE redefine demo end feature demo is require else . do end end
Соответственно, при вызове demo из DERIVED будет проверяться предусловия базового класса. И, только если они не выполняются, предусловия DERIVED.
Постусловия, напротив, определяют обязательства поставщика класса. Следовательно, в производном классе нельзя ослабить постусловия, ведь это означало бы отказ от части обязательств. Поэтому в производном классе постусловия можно только усилить. Из-за чего новые постусловия записываются в секции ensure then :
class BASE feature demo is do ensure . end end class DERIVED inherit BASE redefine demo end feature demo is do ensure then . end end
Соотвественно, при вызове demo из DERIVED будут проверяться и постусловия из базового класса, и постусловия из DERIVED.
Утверждения
check [tag:] . end
позволяет задавать утверждения, которые будут проверяться во время работы программы. Т.е. являются полными аналогами assert в C/C++/D.
Инварианты и варианты цикла
Секции invariant и variant задают условия, которые позволяют проверять корректность цикла. Условия из секции invariant должны выполняться на каждой итерации цикла.
Интересна секция variant . Она задает выражение, производящее целочисленую величину. Эта величина должна оставаться положительной, но должна уменьшаться на каждой итерации цикла.
Нужно отметить, что мне секция variant очень помогла. Поскольку я привык с C-шным циклам for , в которых на автомате пишется ++i , то в Eiffel я постоянно забывал инкрементировать переменную цикла. Т.е. я писал так:
from i := 1 until i > what.count loop Result.put (what.item_code (i), i) end
Соответственно, программа зависала, но требовалось некоторое время чтобы понять, где именно. Добавив секцию variant в каждый такой цикл я стал сразу же получать от Eiffel указания, где мой цикл зависает:
from i := 1 until i > what.count variant what.count + 1 - i loop Result.put (what.item_code (i), i) i := i + 1 end
Все это дело в финальной версии программы
Естественно, что такие тотальные проверки не могут не сказаться на скорости работы программы. И они сказываются. Очень серьезно.
Поэтому в финальной версии программы, по умолчанию, все проверки отключены. Но в свойствах проекта можно указать уровень проверок, которые нужно оставить в программе. Например, только предусловия или предусловия и постусловия, или предусловия+постусловия+инварианты, и т.д. Чем больше проверок оставлено, тем медленнее будет работать финальная версия.
По моим впечатлениям, скорость финальной версии вообще без проверок сравнима со скоростью C/C++/D. Даже включение предусловий не сильно сказывается на скорости работы. Поэтому я бы оставлял в финальной версии проверку предусловий. Хотя Бертран Мейер советует писать так, чтобы программа работала вообще без каких-либо проверок в финальной версии.
Так же проверки серьезно сказываются на размере результирующего кода. Например, рабочая версия со всеми проверками оказывается порядка 9Mb (да, девять мегабайт, даже если это «Hello, World»). Финальная версия без проверок уменьшается до 700Kb. Включение предусловий увеличивает объем где-то на 2-2.5Mb.
Исключения
Исключения в Eiffel есть, но они особенные. Скорее это похоже на механизм сигналов в Unix, хотя при порождении исключения просходит раскрутка стека до первого обработчика, само исключение — это не объект. Скорее это набор неких атрибутов: код исключения и связанное с ним сообщение.
Обработчик исключения в методе может быть только один — он пишется в специальной секции rescue . Обработчик может содержать обычные инструкции и специальную команды retry .
Использование retry предписывает выполнить метод повторно с самого начала (при этом все промежуточные значения, полученные при предшествующем запуске остаются на своих местах). Если команды retry нет, то по завершении секции rescue исключение выбрасывается наружу.
Т.е. либо функция завершается нормально (возможно после нескольких повторных попыток), либо исключение выбрасывается наружу. Т.е. код с использованием исключений выглядит одним из следующих образов:
some_method is local attempts: INTEGER do . -- какие-то действия. rescue if attempts < Max_attempts then -- Можно попробовать повторить операцию еще раз. attempts := attempts + 1 retry end -- В противном случае все попытки исчерпаны и -- исключение выпускается наружу. end another_method is do . -- какие-то действия. rescue . -- какая-то очистка ресурсов без retry. -- После чего исключение выпускается наружу. end
Обобщенное программирование
Eiffel поддерживает обобщенное программирование как в неограниченной (unconstrained), так и в ограниченной (constrained) форме. В первом случае, когда на родовой параметр не накладывается никаких ограничений, обобщенный класс не может вызывать никаких методов у объектов типа родового параметра:
class ARRAY [G] -- G является родовым параметром. feature item (index: INTEGER): G is . end put (v: G; index: INTEGER) is . end . end
Т.е. класс ARRAY [G] способен манипулировать ссылками на объекты типа G, но не способен вызывать у этих объектов никаких методов.
В случае ограниченного обобщенного программирования требуется указать, от какого класса должен наследоваться тип родового параметра:
class MAP [V, K -> HASHABLE] . end
Здесь на тип V не накладывается никаких ограничений, но требуется, чтобы тип K наследовался от HASHABLE (т.е. реализовал контракт HASHABLE ). Поскольку классу MAP нужно уметь получать хеш-код ключа посредством метода hash_code .
Туплы
Eiffel поддерживает понятие тупла -- набора значений разных типов, например, запись вида:
TUPLE [INTEGER; STRING; REAL]
определяет тип тупла с элементами INTEGER , STRING , REAL . Экземпляр тупла в программе записывается в виде заключенного в квадратные скобки набора значений:
[10, "Hello, World", 3.1415]
Элементы тупла могут быть именованными:
local my_type: TUPLE [age: INTEGER; name: STRING; height: REAL] do my_type := [10, "Bob", 1.34] my_type.age := my_type.age + 1 my_type.name := "Robert" end
К элементам тупла можно обращаться по порядковым номерам с помощью метода item .
Тупл вида TUPLE [A, B, C] определяет тип последовательности из, как минимум, трех значений. Поэтому TUPLE [A, B, C] является поддтипом последовательности из, как минимум, двух значний -- TUPLE [A, B] . В свою очередь, TUPLE [A, B] является подтипом TUPLE [A] .
Имена элементов тупла не учитываются при выводе общего типа тупла. Т.е. два тупла, у которых отличаются только имена элементов, но совпадают количество, типы и порядок следования элементов, считаются одинаковыми:
class APPLICATION create make feature make is local t1, t2: TUPLE [n: STRING; a: INTEGER] t3: TUPLE [STRING] do t1 := query_tuple ("first", 21) t2 := query_tuple ("second", 22) t3 := t2 end feature query_tuple (s: STRING; a: INTEGER): TUPLE [name: STRING; age: INTEGER] is do create Result.make Result.name := s Result.age := a end end
Aгенты
Eiffel позволяет сохранять ссылку на метод в переменной или передавать ее в виде параметра в какой-то метод. Выглядит это так:
x.f (agent obj.method)
это означает, передачу ссылки на метод method объекта obj в метод f объекта x . Где метод f может иметь формат:
f( a: PROCEDURE [ANY, TUPLE [ARGS]] )
f( a: FUNCTION [ANY, TUPLE [ARGS], RESULT] )
Внутри f можно сделать вызов агента посредством метода call . Например, пусть есть:
class AGENT_PROVIDER feature demo (i: INTEGER; s: STRING) is do io.put_string ("i=") io.put_integer (i) io.put_string ("; s=") io.put_string (s) io.put_new_line end end class AGENT_CONSUMER feature call_agent (a: PROCEDURE [ANY, TUPLE [INTEGER, STRING]]) is do a.call ( [0, "Hello, World"] ) end end class APPLICATION create make feature make is local producer: AGENT_PROVIDER consumer: AGENT_CONSUMER do create producer create consumer consumer.call_agent (agent producer.demo) end end
Это был пример который использовал открытые аргументы, т.е. значение аргументов задавала вызыващая сторона. Eiffel так же поддерживает закрытые аргументы, т.е. когда часть значений аргументов задается в конструкции agent . Например:
class AGENT_PROVIDER feature demo (who: STRING; i: INTEGER; s: STRING) is do io.put_string ("Who: " + who + "; ") io.put_string ("i=") io.put_integer (i) io.put_string ("; s=") io.put_string (s) io.put_new_line end end class AGENT_CONSUMER feature call_agent (a: PROCEDURE [ANY, TUPLE [INTEGER, STRING]]) is do a.call ( [0, "Hello, World"] ) end end class APPLICATION create make feature make is local producer: AGENT_PROVIDER consumer: AGENT_CONSUMER do create producer create consumer consumer.call_agent (agent producer.demo ("Application", ?, ?)) end end
Здесь аргумент who сделан закрытым, а аргументы i и s -- открытыми.
Как и аргументы, открытыми и закрытыми могут быть цели агентов (т.е. объекты, на чей метод в агентах хранятся ссылки). Выше были показаны примеры закрытых, т.е. заранее определенных целей. Запись вида:
agent obj.f
указывает, что агент связан с методом f объекта obj и ни с кем иным. Запись же:
agent .f
указывает, что агент связан с методом f любого объекта типа TYPE . Однако, в этом случае тип агента будет несколько иным: в метод call агента нужно будет передать тупл, первым элементом которого будет является ссылка на цель вызова:
class AGENT_PROVIDER create make feature make (my_name: STRING) is do name := my_name end feature demo (who: STRING; i: INTEGER; s: STRING) is do io.put_string ("I'm: " + name + "; ") io.put_string ("Who: " + who + "; ") io.put_string ("i=") io.put_integer (i) io.put_string ("; s=") io.put_string (s) io.put_new_line end feature name: STRING end class AGENT_CONSUMER feature call_agent (a: PROCEDURE [ANY, TUPLE [INTEGER, STRING]]) is do a.call ( [0, "Hello, World"] ) end call_agent_from_object ( obj: AGENT_PROVIDER; a: PROCEDURE [ANY, TUPLE [ANY, INTEGER, STRING]]) is do a.call ( [obj, 0, "Hello, World"] ) end end class APPLICATION create make feature make is local first, second: AGENT_PROVIDER consumer: AGENT_CONSUMER do create first.make ("First") create second.make ("Second") create consumer consumer.call_agent (agent first.demo ("Application", ?, ?)) consumer.call_agent_from_object ( second, agent .demo ("Application", ?, ?)) end end
Существует так же возможность создавать т.н. inline agents, т.е. агентов тело которых определяется непосредственно в конструкции agent:
consumer.call_agent ( agent (i: INTEGER; s: STRING) do io.put_string ("inline agent!%N") end)
В этом случае целью агента будет объект Current .
Ковариантность и якоря
При переопределении методов в производных классах в Eiffel разрешается изменять типы аргументов и/или возвращаемого значения на более подходящие, хотя и совместимые с оригинальными. Например, если вначале были классы:
class MESSAGE . end class ENVELOPE . feature msg: MESSAGE end class ENVELOPER feature enveloping_result (m: MESSAGE): ENVELOPE is -- Упаковывает сообщение в конверт и возвращает -- получившийся конверт. do . end end
То затем может появиться набор производных от них классов для поддержки подписанных сообщений:
class SIGNED_MESSAGE inherit MESSAGE . end class SIGNED_ENVELOPE inherit ENVELOPE redefine msg end feature msg: SIGNED_MESSAGE end class SIGNED_MESSAGE_ENVELOPER inherit ENVELOPER redefine enveloping_result end feature enveloping_result (m: SIGNED_MESSAGE): SIGNED_ENVELOPE is ---Упаковывает подписанное сообщение в подписанный -- конверт и возвращает получившийся конверт. do . end end
Такая специализация компонентов msg в классе SIGNED_ENVELOPE и enveloping_result в классе SIGNED_MESSAGE_ENVELOPER является примером ковариантной типизации (covariant typing) -- когда типы изменяются по мере спуска по иерархии наследования.
Ковариантная типизация могла бы приводить к лавинообразной модификации прототипов компонентов в производных классах. Например, раз класс SIGNED_ENVELOPE хранит SIGNED_MESSAGE вместо просто MESSAGE , значит в своем конструкторе он должен получать ссылку не на MESSAGE , а на SIGNED_MESSAGE . Равно как если бы базовый класс ENVELOPE имел другие методы, получающие или возвращающие MESSAGE , то SIGNED_ENVELOPE должен был бы предоставить их новые версии (даже если бы новые версии полностью совпадали с предыдущими). Т.е. если был класс:
class ENVELOPE create make feature make (m: MESSAGE) is do msg := m end msg: MESSAGE is_same_message (m: MESSAGE): BOOLEAN is do Result := msg.is_equal (m) end clone_message: MESSAGE is do Result := clone (msg) end end
то в новом классе SIGNED_ENVELOPE могло бы потребоваться создание точных копий make , is_same_message и clone_message , только оперирующих типом SIGNED_MESSAGE .
Для того, чтобы избежать этого бесполезного дублирования кода в Eiffel существует понятие закрепленных типов или, в дословном переводе, якорных типов (anchored types). Т.е. тип какого-то компонента объявляется якорем, а все остальные объявляются подобными на него, для чего используется специальная конструкция like .
Так, в примере с ENVELOPE все вращается вокруг атрибута msg , следовательно его можно сделать якорем. А все остальные ссылки на тип MESSAGE заменить конструкцией like msg :
class ENVELOPE create make feature make (m: like msg) is do msg := m end msg: MESSAGE is_same_message (m: like msg): BOOLEAN is do Result := msg.is_equal (m) end clone_message: like msg is do Result := clone (msg) end end
При таком подходе все, что нужно сделать в классе SIGNED_ENVELOPE -- это сменить тип якоря msg , смена актуальных типов для make , is_same_message и clone_message произойдет автоматически:
class SIGNED_ENVELOPE inherit ENVELOPE create make end
И все, больше ничего в SIGNED_ENVELOPE изменять не нужно (если только не добавить в него какую-то специфическую функциональность).
Конструкцию like можно применять и к Current , например:
class ENVELOPE . feature copy (other: like Current) is do msg := clone (other.msg) end . end
т.е. метод copy класса ENVELOPE ожидает, что тип аргумента other будет соответствовать типу ENVELOPE .
Константы и once-методы
Константы и уникальные значения
Константы декларируются в Eiffel в виде:
Const_name: TYPE is VALUE
Ok: INTEGER is 0 File_not_found: INTEGER is 2 File_busy: INTEGER is 3 Ready_question: STRING is "Are you ready?"
Если нужно определить несколько целочисленных констант, каждое из которых должно иметь уникальное значение (но не важно какое именно), то можно использовать конструкцию unique :
Ok, File_not_found, File_busy: INTEGER is unique
При работе с константами в Eiffel сказывается то, что Eiffel объектный язык без глобальных переменных, статических методов или свободных функций. Так, если какой-нибудь класс, например, FILE определяет набор констант, то получить доступ к значению конкретной константы можно одним из следующих подходов:
Через объект типа FILE :
local f: FILE do f.open ("myfile.txt") if f.Ok = f.open_result then . end . end
Через специальный синтаксис .Constant :
local f: FILE do f.open ("myfile.txt") if .Ok = f.open_result then . end . end
Через наследование от класса FILE :
class MY inherit FILE export all end feature update_my_file is do open ("myfile.txt") if Ok = open_result then . end . end end
Once-методы
Once-методы в Eiffel выполняются всего один раз -- во время первого обращения к ним. Если once-метод является функцией, то возвращенное ей значение будет сохранено и будет возвращаться при всех последующих обращениях к ней.
Декларация once-метода отличается от деклараций обычных методов всего лишь использованием ключевого слова once вместо do :
feature console_window: WINDOW is once . end
Once-функции являются способом создания значений, разделяемых всеми экземплярами класса. Например, io является once-функцией базового для всех класса ANY , благодоря чему любое обращение к io в любом из объектов приводит к использованию одного и того же значения.
Переопределение операторов и псевдонимы методов
Eiffel позволяет определять одинаковую реализацию для нескольких методов с разными именами:
class OUPUT_STREAM feature put_real, putreal (v: REAL) is . end put_string, put_line, putline (v: STRING) is . end . end
При этом определяются независимые друг от друга методы с одинаковыми сигнатурами и одинаковой первоначальной реализацией. В последствии любой из них может быть переопределен независимо от остальных.
Поскольку в Eiffel-программе есть только объекты, которые взаимодействуют друг с другом посредством вызова методов, в Eiffel разрешено переопределение операторов для того, чтобы было удобно записывать математические выражения. Переопределение выполняется посредством назначения псевдонимов методам:
class COMPLEX feature plus alias "+" (v: COMPLEX) is . end . end
Переопределять можно не только стандартный набор арифметических операторов. В качестве псевдонима может быть задана любая последовательность символов, если только она начинается с одного из стандартных символов. Например, можно определить собственный оператор | :
class MESSAGE create make feature make (msg_text: STRING) is require valid_text: msg_text /= Void do text := msg_text end text: STRING end class ENVELOPE create make feature make (a_receiver: STRING; a_msg: MESSAGE; a_sender: STRING) is require valid_receiver: (a_receiver /= Void) and then (0 < a_receiver.count) valid_msg: a_msg /= Void valid_sender: (a_sender /= Void) and then (0 < a_sender.count) do receiver := a_receiver msg := a_msg sender := a_sender end receiver: STRING msg: MESSAGE sender: STRING end class ENVELOPER create make feature make (a_receiver: STRING; a_sender: STRING) is require valid_receiver: (a_receiver /= Void) and then (0 < a_receiver.count) valid_sender: (a_sender /= Void) and then (0 < a_sender.count) do receiver := a_receiver sender := a_sender end enveloping_result alias "|receiver: STRING sender: STRING end class APPLICATION create make feature make is local enveloper: ENVELOPER enveloped_msg: ENVELOPE do create enveloper.make ("Alice", "Bob") enveloped_msg := enveloper |.make ("Hello!")) io.put_string ("Receiver: " + enveloped_msg.receiver + "; ") io.put_string ("Sender: " + enveloped_msg.sender + "; ") io.put_string ("Msg: " + enveloped_msg.msg.text) io.put_new_line end end
В данном примере для класса ENVELOPER переопределен нестандартный оператор | , который является синонимом метода enveloping_result .
Возможность назначения псевдонимов с нестандартными именами предназначена для того, чтобы можно было придумывать обозначения, специфические для какой-нибудь конкретной прикладной области (например, для физических расчетов, где могут потребоваться операторы вида |-| или ).
Примечание. Я столкнулся с тем, что в книге Object Oriented Software Construction вводится один набор специальных символов, которые могут начинать переопределенный оператор. В ECMA стандарте вообще не делается никаких ограничений на этот счет. Но EiffelStudio 6.0.6 (GPL-Windows версия) не позволяет, например, начинать оператор с символа . В другом источнике утверждается, что переопределенные операторы могут начинаться с одного из следующих символов: @ # | & .
Eiffel выделяет особую форму псевдонимов, т.н. bracket alias, для переопределения [] . Эта форма удобна для организации общепринятого доступа к векторам, матрицам, ассоциативным таблицам:
class VECTOR [G] feature item alias "[]" (index: INTEGER): G is . end . end
что позволяет извлекать элементы вектора традиционным способом:
local v: VECTOR [REAL] do . io.put_real (v [1]) . end
Для того, чтобы предоставить общеупотребительную форму для изменения элемента по индексу используется специальный синтаксис определения псевдонима с использованием ключевого слова assign :
class VECTOR [G] feature item alias "[]" (index: INTEGER): G assign put do . end put (v: G; index: INTEGER) is do . end end
Отличительной чертой переопределения операторов в Eiffel является то, что реализация оператора должна быть выполнена только в форме запроса, а не команды. Т.е. если следовать заповеди, что запросы не могут иметь побочных эффектов, то и переопределенные операторы в Eiffel не могут приводить к побочным эффектам.

© 2007 Е.А. Охотников
LastChangedDate: 2007-08-06 13:32:55
К сожалению я не силен в грамматике, поэтому если вы увидели здесь какие-либо орфографические или синтаксические ошибки, то не сочтите за труд -- сообщите мне. Ваша помощь поможет мне сделать этот текст гораздо лучше.
Eiffel (язык программирования) - Eiffel (programming language)
Eiffel - это объектно- ориентированный язык программирования, ориентированный Бертраном Мейером (сторонником объектной ориентации и автором Конструирование объектно-ориентированного программного обеспечения ) и Eiffel Software. Мейер придумал язык в 1985 году с целью повышения надежности программного обеспечения; первая версия стала доступной в 1986 году. В 2005 году Eiffel стал стандартизированным языком ISO.
Многие концепции, принятые введенными Эйфелем, позже свое отражение в Java, C# и других языках. Новые идеи дизайна языка, в частности, посредством стандартизации Ecma / ISO, продолжают внедряться в язык Eiffel.
- 1 Характеристики
- 2 Цели проектирования
- 2.1 Предпосылки
- 2.2 Реализации и среды
- 2.3 Спецификации и стандарты
- 3.1 Общая структура
- 3.1.1 Область действия
- 3.1.2 «Привет, мир!»
Характеристики
Ключевые характеристики языка Eiffel включают:
- объектно-ориентированные программы, которые служат основой для декомпозиции.
- контракту Встроенный интегрирован с другими языковыми конструкциями.
- Автоматическое управление памятью, обычно реализуемое посредством сборки мусора.
- Наследование, включая множественное наследование, переименование, переопределение, «Выбор», несоответствующее наследование и другие механизмы, предназначенные для обеспечения безопасности наследования.
- Ограниченное и неограниченное универсальное программирование
- Единая система типов , обрабатывающая семантику как значения, так и ссылки, в которых все типы, включая базовые типы, такие как INTEGER, основаны на классах.
- Статическая типизация
- Пустая безопасность или статическая защита от нулевых ссылок с помощью механизма указанных типов.
- Агенты или объекты, которые обертывают управление, связаны с подключениями и лямбда-исчислением.
- Один раз подпрограммы или подпрограммы, оцениваемые только один раз, для совместного использования объектов и децентрализованной инициализации.
- Синтаксис на основе ключевых слов в традиции АЛГОЛ / Паскаль, но без параметров, поскольку точки с запятой являются необязательными, с синтаксисом операторами, доступным для подпрограмм.
- Нечувствительность к регистру
- Простое параллельное объектно-ориентированное программирование (SCOOP ) облегчает создание нескольких, одновременно активных исполнительных машин на уровне абстракции выше конкретных деталей этих транспортных средств (например, несколько потоков без специального управления мьютексами).
Цели проектирования
Эйфель делает упор на декларативные утверждения, а не на процедурный код, и устранить необходимость в инструкциях по ведению бухгалтерского учета.
Эйфель избег уловок кодирования или методов кодирования, предназначенных для оптимизации подсказок компилятору. Цель состоит не только в том, чтобы сделать код более читаемым, но и в том, чтобы позволить программистам сконцентрироваться на важных аспектах программы, не в деталях реализации. Простота многократного использования универсального универсального устройства Eiffel для использования в надежных решениях вычислительных задач. Компиляторы для компьютерных программ, написанных на Eiffel, специальные методы оптимизации, такие как автоматическое встраивание, которое освобождает программиста от части бремени оптимизации.
Предпосылки
Eiffel изначально был разработан Eiffel Software, компанией, основанной Бертраном Мейером. Построение объектно-ориентированного программного обеспечения содержит подробное описание концепций и теории объектной технологии, которые приводят к разработке Эйфеля.
Целью проектирования, лежащей в основе языка, библиотек и методов программирования, Eiffel, позволяет создать надежные, многоразовые программные модули. Eiffel поддерживает множественное наследование, универсальность, полиморфизм, инкапсуляцию, типобезопасные преобразования и ковариацию параметров. Самый важный вклад Эйфеля в программного обеспечения - это проектирование по контракту (DbC), в котором утверждение, предварительные условия, постусловия и инварианты классов используются для корректности программы без ущерба для эффективности.
Дизайн Эйфеля основан на теории объектно-ориентированного программирования с незначительным другим парадигм или заботой о устаревшего кода. Эйфель формально поддерживает абстрактные типы данных. По замыслу Эйфеля, программный текст должен иметь возможность воспроизводить свою проектную документацию из самого текста, используя формализованную работу «абстрактного типа данных».
Реализации и среды
EiffelStudio - это интегрированная среда разработки, доступная либо по с открытым исходным кодом, либо по коммерческой лицензии. Он предлагает объектно-ориентированную среду для разработки программного обеспечения. EiffelEnvision - это подключаемый модуль для Microsoft Visual Studio, который позволяет пользователям редактировать, компилировать и отлаживать проекты Eiffel из среды Microsoft Visual Studio IDE. Доступны пять других реализаций с открытым исходным кодом: Tecomp "The Eiffel Compiler"; Гобо Эйфель; SmartEiffel, реализация GNU, основанная на более старой версии языка; LibertyEiffel, на основе компилятора SmartEiffel; и Visual Eiffel.
Некоторые другие языки программирования включают элементы, впервые представленные в Eiffel. Sather, например, изначально был основан на Eiffel, но с тех пор разошелся и теперь включает несколько функций функционального программирования. Язык интерактивного обучения Blue, предшественник BlueJ, также основан на Eiffel. Apple Media Tool включает язык Apple Media на основе Eiffel.
Спецификации и стандарты
Определение языка Eiffel является международным стандартом ISO. Стандарт был разработан ECMA International, которая впервые утвердила стандарт 21 июня 2005 г. как Стандарт ECMA-367, Eiffel: язык анализа, проектирования и программирования. В июне 2006 года ECMA и ISO приняли вторую версию. В ноябре 2006 года ISO впервые опубликовала эту версию. Стандарт можно найти и бесплатно использовать на сайте ECMA. Версия ISO идентична во всех отношениях, кроме форматирования.
Eiffel Software, Tecomp "Eiffel Compiler" и разработчик библиотеки Eiffel Gobo взяли на себя обязательство внедрить стандарт; EiffelStudio 6.1 и «Компилятор Eiffel» от Eiffel Software реализуют некоторые из основных новых механизмов, в частности, встроенные агенты, команды присваивания, скобочную нотацию, несоответствующее наследование и присоединенные типы. Команда SmartEiffel отвернулась от этого стандарта, чтобы создать свою собственную версию языка, которая, по их мнению, ближе к оригинальному стилю Eiffel. Object Tools не раскрывает, будут ли будущие версии его компилятора Eiffel стандарту. LibertyEiffel реализует диалект где-то посередине между языком SmartEiffel и стандартом.
Стандарт цитирует следующие предшествующие спецификации языка Eiffel:
- Бертран Мейер: Eiffel: The Language, Prentice Hall, вторая печать, 1992 г. (первая печать: 1991 г.)
- Бертран Мейер: Standard Eiffel (редакция предыдущей статьи), текущая, с 1997 г. по настоящее время, на странице Бертрана Мейера в ETL3 и
- Бертран Мейер: Построение объектно-ориентированного программного обеспечения, Prentice Hall: первое издание, 1988 г.; второе издание, 1997 г.
- Бертран Мейер: Touch of Class: Обучение правильному программированию с помощью объектов и контрактов, Springer-Verlag, 2009 г. ISBN 978-3-540-92144-8 lxiv + 876 страниц Полноцветная печать, множество цветных фотографий
Текущая версия стандарта от июня 2006 г. содержит некоторые несоответствия (например, ковариантные переопределения). Комитет ECMA еще не объявил никаких сроков и указаний по устранению несоответствий.
Синтаксис и семантика
Общая структура
«система» или «программа» Эйфеля - это совокупность классов. Выше классов Eiffel определяет кластер, который, по сути, представляет собой группу классов и, возможно, подкластеров (вложенных кластеров). Кластеры - это не синтаксическая конструкция языка , а скорее стандартное организационное соглашение. Обычно программа Eiffel организована с каждым классом в отдельном файле, каждый кластер - в каталоге, содержит файлы классов. В этой организации подкластеры - это подкаталоги. Например, согласно стандартным организационным системам, xe может быть именем файла, который определяет класс с именем X.
Класс содержит функции, которые похожи на «процедуры», «члены», «Атрибуты» или «методы» в других объектно-ориентированных языках программирования. Класс также определяет свои инварианты и содержит другие свойства, такие как раздел «примечания» для документации и метаданных. Стандартные данные Эйфеля, такие как INTEGER , STRING и ARRAY , сами по себе являются классами.
Каждая система должна класс, обозначенный как «иметь», с одной из его процедур создания обозначенных как «корневая процедура». Выполнение системы состоит из создания экземпляра корневого класса и выполнения его процедуры. Как правило, при этом новые объекты вызываются новые функции и т. Д.
У Eiffel есть пять исполняемых инструкций: назначение, создание объекта, вызов подпрограммы, условие и итерация. Управляющие структуры Eiffel строго следят за соблюдением структурного программирования: каждый блок имеет ровно одну запись и ровно один выход.
Scoping
В отличие от объектов многих объектов, ориентированных на языков, но, как и Smalltalk, Eiffel не разрешает никакого присваивания атрибутам объектов, за исключением характеристик объекта, которые являются практическим выполнение принципа сокрытия информации или абстракции данных, требуемых формальных интерфейсов для мутации данных. Чтобы перевести его на язык других объектно-ориентированных языков программирования, все атрибуты Eiffel являются «защищенными», а для изменения значений клиентскими объектами необходимы «установщики». Результатом этого является то, что «установщики» могут и обычно реализуют инварианты, для которых Eiffel использует синтаксис.
Хотя Eiffel не разрешает прямой доступ к функции класса клиентского класса, он допускает определение «команды назначенного», например:
some_attribute: SOME_TYPE assign set_some_attribute set_some_attribute (v: VALUE_TYPE) - Установить значение некоторого атрибута равным `v '. do some_attribute: = v end
Хотя небольшой поклон всему сообществу разработчиков, чтобы разрешить что-то похожее на прямой доступ (например, тем самым нарушая принцип сокрытия информации), такая практика опасна, поскольку она скрывает или запутывает реальность "установщика" На практике лучше перенаправить вызов сеттеру, чем предполагать прямой доступ к такой функции, как some_attribute , как в приведенном выше примере кода.
В отличие от других языков, имеющих понятие «общедоступный» »,« Защищенный »,« частный »и т. Д., Eiffel использует методы экспорта, чтобы более точно контролировать видимости между классами клиентов и поставщиков. Аналогично слову «защищенный» в других языках. Область применения, примененная таким образом к «набору функций» (например, все, что находится ниже ключевого слова «характеристика» до следующего ключевого слова набора функций или до конца класса), может быть изменено в классах-потомках с помощью ключевого слова «экспорт».
feature - Инициализация default_create - Инициализировать новый "нулевой" десятичный экземпляр. do make_zero end
В качестве альтернативы, отсутствие объявления экспорта подразумевает и аналогично "общедоступной" области видимости других языков.
feature - Константы
Наконец, можно выборочно и точно контролировать область видимости для любого класса во вселенной проекта Eiffel, например:
feature - Доступ
, компилятор только классам, перечисленным в фигурных скобках, получить доступ к функциям внутри группы функций (например, DECIMAL, DCM_MA_DECIMAL_PARSER, DCM_MA_DECIMAL_HANDLER).
«Привет, мир!»
Внешний вид языка программирования часто передается с помощью программы «Привет, мир!». Такая программа, написанная на Eiffel, может быть:
класс HELLO_WORLD create make feature make do print ("Hello, world!% N") end endЭта программа содержит класс HELLO_WORLD . Конструктор (процедура создания) для класса с именем make вызывает системную библиотеку print для записи "Hello, world! " сообщение на выход.
Дизайн по контракту
Концепция дизайна по контракту является центральной для Eiffel. Контракты утверждают, что должно быть истинным до завершения подпрограммы. Контракты, инвариантные для класса, определяют, какие утверждения должны быть до и после доступа к любым функциям класса (как к процедурам, так и к атрибутам). Более, контракты кодифицируют в исполняемом коде предположения разработчиков и дизайнеров в среде функций класса или класса в целом с помощью инварианта.
Компилятор Eiffel разработан для включения контрактов функций и классов на различных уровнях. EiffelStudio, например, выполняет все контракты функций и классов во время выполнения в «режиме Workbench». Когда исполняемый файл создается, компилятор получает указание через файл настроек проекта (например, файл ECF) либо включить, либо исключить любой набор контрактов. Таким образом, исполняемый файл может быть скомпилирован для включения или исключения любого уровня контракта, тем самым непрерывными уровнями модульного и интеграционного тестирования. Более того, контракты могут работать непрерывно и методично с помощью функций автотеста в EiffelStudio.
Механизмы проектирования по объединению интегрированных языков и направляют переопределение функций в наследовании:
- Обычное предварительное условие: предварительное условие может быть ослаблено только наследованием; любой вызов, который соответствует требованиям предка, соответствует требованиям потомка.
- Обычное постусловие: Постусловие может быть усилено только наследованием; любой результат, гарантированный предком, по-прежнему предоставляется потомком.
- Инвариант класса: условия, которые должны быть произведены после создания объекта и после вызова процедуры экспортированного класса. Инвариант проверяет так часто, это делает его одновременно и самой дорогой, и самой мощной формой условий или контракта.
Кроме того, язык поддерживает «инструкции проверки» (своего рода «утверждение»), инварианты цикла, и варианты цикла (гарантирующие завершение цикла).
Void-security
Void-security, как и статическая типизация, является еще одним средством повышения качества программного обеспечения. Безопасное программное обеспечение защищено от ошибок времени выполнения, вызванных вызовами пустых ссылок, и поэтому будет более надежным, чем программное обеспечение, которое может вызвать вызовы пустых целей. Полезна аналогия со статической типизацией. Фактически, возможность защиты от пустот можно рассматривать как расширение системы типов или шаг за пределы статической типизации, потому что механизм обеспечения защиты от пустот интегрирован в систему типов.
Защиту от недействительных целевых вызовов можно увидеть с помощью понятий присоединения и (по расширению) отсоединения (например, ключевое слово detachable). Возможности void-safe можно увидеть в короткой переработке приведенного выше примера кода:
some_attribute: detachable SOME_TYPE use_some_attribute - Установить значение some_attribute на `v '. do, если some_attribute прикреплен как l_attribute, затем do_something (l_attribute) end end do_something (a_value: SOME_TYPE) - Сделайте что-нибудь с ʻa_value '. делать. делать что-то с ʻa_value '. end
В приведенном выше примере кода показано, как компилятор может статически определять надежность того, будет ли some_attribute присоединен или отсоединен в точке его использования. Примечательно, что присоединенное ключевое слово допускает «локальное вложение» (например, l_attribute ), которое ограничено только блоком кода, заключенным в конструкцию if-statement. Таким образом, в этом небольшом блоке кода можно статически гарантировать, что локальная переменная (например, l_attribute ) не является недействительной (то есть является недействительной).
Возможности: команды и запросы
Основная характеристика класса состоит в том, что он определяет набор функций: поскольку класс представляет набор объектов времени выполнения или «экземпляров», особенность - это операция над этими объектами. Есть два типа функций: запросы и команды. Запрос предоставляет информацию об экземпляре. Команда изменяет экземпляр.
Различие между командой и запросом важно для метода Эйфеля. В частности:
- Принцип унифицированного доступа : с точки зрения программного клиента, выполняющего вызов функции класса, независимо от того, является ли запрос атрибутом (значением поля) или функцией (вычисляемым значением), не должно не имеет значения. Например, a_vehicle.speed может быть атрибутом, к которому осуществляется доступ кобъекту a_vehicle , или он может быть вычислен функцией, которая делит расстояние на время. Обозначения одинаковы в обоих случаях.
- Принцип разделения команд и запросов : Запросы не должны проверять экземпляр. Это не языковое правило, методологический принцип. Итак, в хорошем стиле Эйфеля нельзя найти функции «получить», которые что-то меняют и возвращают результат; вместо этого существуют команды (процедуры) для изменения объектов и запросов для получения информации об объекте в результате предыдущих изменений.
Перегрузка
Eiffel не допускает перегрузку аргумента . Каждое имя функции в классе всегда сопоставляется с определенной функцией в классе. Одно имя в рамках одного класса означает одно. Этот выбор дизайна помогает читаемости классов, неправильная причина неоднозначности в отношении того, какая процедура будет вызвана вызовом. Это также упрощает языковой механизм; в частности, это делает возможным механизм множественного наследования Эйфеля.
Имена, конечно, можно повторно использовать в разных классах. Например функция, плюс (вместе с его инфиксным псевдонимом«+») определена в нескольких классах: INTEGER, REAL, STRING и т. Д..
Универсальность
Универсальный класс - это класс, который различается по типу (например, LIST [PHONE], список номеров телефонов; ACCOUNT [G->ACCOUNT_TYPE], что позволяет использовать ACCOUNT [SAVINGS] и АККАУНТ [ПРОВЕРКА] и т. д.). Классы могут быть универсальными, что означает, что они параметры по типам. Общие параметры заключаются в квадратные скобки:
класс LIST [G].
G известен как «формальный универсальный параметр». (Эйфель резервирует «аргумент» для подпрограмм и использует «параметр» только для общих классов.) С таким объявлением G представляет внутри класса произвольный тип; поэтому функция может возвращать значение типа G, процедура может принимать аргумент этого типа:
item: G do. end put (x: G) do. end
The LIST [INTEGER] и LIST [WORD] являются «родовыми производными» этого класса. Разрешенные комбинации (с n: INTEGER , w: WORD , il: LIST [INTEGER] , wl: LIST [WORD] ) это:
n: = il.item wl.put (w)
INTEGER и WORD - «фактические общие параметры» в этих общих производных.
Также возможно иметь «ограниченные» формальные параметры, для которых фактический параметр должен наследовать от данного класса, «ограничение». Например, в классе
HASH_TABLE [G, KEY ->HASHABLE]
вывод HASH_TABLE [INTEGER, STRING] действителен, только если STRING наследуется от HASHABLE (как и в типичных библиотеках Eiffel). Внутри класса наличие KEY , ограниченного HASHABLE , означает, что для x: KEY можно применить к x все функции HASHABLE , как в x.hash_code .
Основы наследования
Чтобы наследовать от одного или нескольких других классов будет наследовать наследовать в начало:
класс C наследует AB -. Остальная часть объявления класса.
Класс может переопределить (переопределить) некоторые или все унаследованные функции. Это должно быть явно объявлено в начале класса с помощью подпункта переопределить в предложении наследования, как в
класс C наследовать A переопределить f, g, h end B переопределить u, v end
См. Полное обсуждение наследования Эйфеля.
Отложенные классы и функции
Классы могут быть определены с отложенным классом , а не с классом , чтобы указать, что класс не может быть создан напрямую. Классы, не поддерживающие создание экземпляров, называются абстрактными классами в некоторых других объектно-ориентированных языках программирования. На языке Эйфеля можно создать экземпляр только «эффективного» класса (он может быть потомком отложенного класса). Функцию также можно отложить, используя слово deferred вместо предложения do . Если у класса есть какие-либо отложенные функции, он должен быть объявлен как отложенный; однако класс без отложенных функций может, тем не менее, отложенным.
Отложенные классы играют примерно ту же роль, что и многие теоретики объектно-ориентированного программирования считают, что интерфейсы сами по себе являются ответом на отсутствие множественных наследования в Java ( которое есть у Эйфеля).
Переименование
Класс, унаследованный от одного или нескольких других, получает все свои функции по умолчанию под своими исходными именами. Однако он может изменить их имена с помощью пунктов переименовать . Это требуется в множественном наследовании, если есть конфликт имен между унаследованными функциями; без переименования результирующий класс нарушит вышеуказанным принципом перегрузки и, следовательно, будет недействительным.
Кортежи
Типы кортежей можно рассматривать как простую форму класса, предоставляющую только атрибуты и условия «установщика». Типичный тип кортежа читается как
ТУПЛЕ [имя: СТРОКА; вес: РЕАЛЬНЫЙ; дата: DATE]
и другое руководство для описания Простое понятие записи о рождении, если класс не нужен. Экземпляр такого кортежа - это просто последовательность значений с заданными типами, указанными в скобках, например
["Brigitte", 3.5, Last_night]
К компоненту такого кортежа можно обращаться, как если бы кортеж теги были атрибутами класса, например, если т был назначен вышеуказанный кортеж, то т. вес имеет значение 3,5.
Благодаря понятию команда присваивания (см. Ниже), точечная такая нотация также может присвоить компоненты кортежа, как в
t.weight: = t.weight + 0.5
Теги кортежа являются необязательными, поэтому также можно записать тип кортежа как TUPLE [STRING, REAL, DATE] . (Некоторые компиляторах это единственная форма кортежа, поскольку теги были введены в стандарте ECMA.)
Точная спецификация, например, TUPLE [A, B, C] заключается в том, что он является следовать, по меньшей мере, из трех элементов, первые три из системы к типам A , B , C соответственно. В результате ТУПЛ [A, B, C] соответствует (может быть назначен) ТУПЛ [A, B] , к ТУПЛ [A] и TUPLE (без параметров), самый верхний тип кортежа, соответствуют все типы кортежей.
Агенты
«агентный» механизм Эйфеля превращает операции в объекты. Этот механизм можно использовать для итераций, программы, программы событиями, и других контекстов, в которых используется передача операций по структуре программы. Другие языки программирования, особенно те, которые делают упор на функциональное программирование, допускают аналогичный шаблон с использованием продолжений, замыканий или генераторов ; Агенты Эйфеля подчеркивают объектно-ориентированную парадигму языка и используют синтаксис и семантику, аналогичные блокам кода в Smalltalk и Ruby.
. Например, для выполнения блока my_action для каждого элемента my_list можно написать:
my_list.do_all (agent my_action)
Чтобы выполнить my_action только для элементов, удовлетворяющих my_condition , можно добавить ограничение / фильтр:
my_list.do_if (agent my_action, agent my_condition)
В этих примерах my_action и my_condition являются подпрограммами. Добавление к ним префикса агент дает объект, который соответствует соответствующей подпрограмме со всеми ее свойствами, используя вызов с поставщиком аргументами. Итак, если a представляет этот объект (например, потому что a является аргументом для do_all ), инструкция
a.call ([x])
вызовет исходную функцию с аргументом x , как если бы мы вызвали исходную функцию напрямую: my_action (x) . Аргументы для вызов передаются в виде кортежа, здесь [x] .
Можно оставить некоторые аргументы для агента открытыми и сделать другие закрытыми . Открытые аргументы передаются в качестве аргументов в call : они во время использования агента. Закрытые аргументы во время определения агента. Например, если action2 имеет два аргумента, итерация
my_list.do_all (agent action2 (?, Y))
повторяет action2 (x, y) для принятия значения x , где второй аргумент остается установленным на y . Знак вопроса ? указывает на открытый аргумент; y - закрытый аргумент агента. Обратите внимание, что основной синтаксис agent f является сокращением для agent f (. ) со всеми открытыми аргументами. Также возможно сделать цель открытой с помощью записи ? , где T - это тип цели.
Различие между открытыми и закрытыми опдами (операнды = аргументы + цель соответствует различию между связанными и свободными переменными в лямбда-исчислении. Выражение агента, такое как action2 (?, Y) с некоторыми закрытыми операндами и некоторыми открытыми, соответствует версии исходной операции curried для закрытых операндов.
Механизм агента также позволяет определять агент без ссылок на существующую подпрограмму (например, my_action , my_condition , action2 ) через встроенные агенты, как в
my_list.do_all (агент (s: STRING) требует not_void: s / = Void do s.append_character (',') гарантирует добавление: s.count = old s.count + 1 end)Переданный здесь встроенный агент может использовать все обычные процедуры, предварительное условие, постусловие, предложение восстановления (здесь не используется). Это позволяет избежать определения подпрограмм, когда это нужно, - это вычисления, которые должны быть заключены в агент. Это полезно, в частности, для контрактов, как в инвариантном предложении, которое выражает, что все элементы списка положительны:
my_list.for_all (agent (x: INTEGER): BOOLEAN do Result: = (x>0) end)
Текущий механизм агента оставляет возможность ошибки типа времени выполнения (если процедура с аргументами передается агенту, ожидаемое m аргументов с m valid_arguments из call . Доступно несколько предложений по чисто статическому исправлению этой проблемы, в том числе предложение об изменении языка, предложенное Рибетом и др.
Один раз, подпрограммы
Результат подпрограммы может быть кэширован с помощью один раз использовать слово выполнить . Не первые вызовы подпрограммы не требуют дополнительных вычислений или выделения ресурсов, а просто выполняют вычисленный результат. Распространенным шаблоном для «однократных функций» является предоставление общих объектов; первый вызов создаст объект, последующие вернут ссылку на этот объект. Типичная схема:
shared_object: SOME_TYPE после создания Result.make (args) - Создает объект и возвращает ссылку на него через `Result '. end
Возвращаемый объект - Результат в примере - сам может быть изменяемым, но его ссылка остается той же.
Часто «однократные подпрограммы» запускают инициализацию: несколько вызовов библиотеки могут запускать процедуры инициализации, но только первый вызов будет выполнять требуемые. С помощью этого шаблона инициализацию можно децентрализовать, избегая необходимости в специальном модуле инициализации. «Один раз подпрограммы» аналогичны по назначению и действию с одноэлементным шаблоном во многих языках программирования, а также с шаблоном Борга, используемым в Python.
По умолчанию «однократная процедура» вызывается один раз для каждого потока. Семантику можно настроить на один раз для процесса или один раз на объекте, уточни его с помощью «однократного ключа», например один раз («ПРОЦЕСС») .
Конверсии
Eiffel предоставляет механизм, позволяющий конвертировать между различными типами. Механизмы сосуществуют с наследованием и дополняют его. Чтобы избежать путаницы между двумя механизмами, в дизайне используется следующий принцип:
(Принцип преобразования) Тип не может одновременно соответствовать и преобразовывать в другой.
Например, ГАЗЕТА может соответствовать PUBLICATION , но INTEGER преобразуется в REAL (и не наследуется от него).
Механизм преобразования просто обобщает специальные правила преобразования (например, между INTEGER и REAL ), которые существуют в большинстве языков программирования, что делает их применимыми к любому типу. пока соблюдается вышеуказанный принцип. Например, класс DATE может быть объявлен для преобразования в STRING ; это позволяет создать строку из даты просто через
my_string: = my_date
в качестве ярлыка для использования явного создания объекта с процедурой преобразования:
create my_string.make_from_date (my_date)
Чтобы первая форма стала синонимом второй, достаточно перечислить процедуру создания (конструктор) make_from_date в предложении convert в начале класса.
В качестве другого примера, если существует такая процедура преобразования, указанная из TUPLE [день: INTEGER; месяц: STRING; year: INTEGER] , тогда можно напрямую присвоить кортеж дате, вызывая соответствующее преобразование, как в
Bastille_day: = [14, "July", 1789]
Обработка исключений
Exception Обработка в Eiffel основана на принципах проектирования по контракту. Например, исключение возникает, когда вызывающая подпрограмма не может удовлетворить предварительное условие или когда процедура не может гарантировать обещанное постусловие. В Eiffel обработка исключений не используется для потока управления или для исправления ошибок ввода данных.
Обработчик исключений Eiffel определяется с помощью ключевого слова rescue. В разделе восстановления использовать слово повторить снова выполнение функции. Например, следующая процедура отслеживает количество попыток ее выполнения и повторяет только определенное количество раз:
connect_to_server (сервер: SOCKET) - подключиться к серверу или отказаться от него после 10 попыток. require server / = Void, а затем server.address / = Аннулировать локальные попытки: INTEGER do server.connect sure connected: server.is_connected rescue if пытается < 10 then attempts := attempts + 1 retry end end
Этот пример, возможно, ошибочен для чего-либо, кроме простейших программ, однако, из-за сбоя соединения следовало ожидать. Для всех программ было бы лучше использовать имя подпрограммы, например, try_connecting_to_server, и постусловие не обещало бы соединение, оставляя вызывающую возможность возможности выполнить соответствующие шаги, если соединение не было открыто.
Параллелизм
Доступен ряд сетевых и потоковых библиотек, таких как EiffelNet и EiffelThreads. Модель параллелизма для Eiffel, основанная на концепции проектирования по контракту, - это SCOOP, или простое параллельное объектно-ориентированное программирование, которое еще не является частным официальным определением языка, но доступно в EiffelStudio. CAMEO - это (нереализованная) вариация SCOOP для Eiffel. Параллелизм также взаимодействует с исключениями. Асинхронные исключения могут вызвать завершение проблемы (когда подпрограмма вызывает исключение после того, как ее вызывающая сторона вызывает проблему, вызывающую проблему).
Синтаксис оператора и скобки, командываивания
Взгляд Эйфеля на вычисления полностью объектно-ориентирован в ощущение, что каждая операция относится к объекту, «цели». Так, например, такое добавление, как
a + b
, концептуально понимается так, как если бы это был вызов метод
a.plus (b)
с целью a , функция плюс и аргумент b .
Конечно, первое является обычным синтаксисом и обычно предпочтительнее. Синтаксис оператора позволяет использовать любую форму путем объявления функции (например, в INTEGER , но это применимо к другим базовым классам и может предложить любой другой, для которых подходит такой оператор):
плюс псевдоним "+" (другое: INTEGER): INTEGER -. Обычное объявление функции. end
Диапазон операторов, которые могут быть в качестве псевдонима, широкие; они включают предопределенные операторы, такие как «+», а также «свободные операторы», состоящие из не буквенно-цифровых символов. Это позволяет создать инфиксные и префиксные обозначения, например, в математических и физических приложениях.
Каждый класс может иметь одну функцию с псевдонимом "", оператор "скобки", позволяя обозначение a [i. ] в качестве синонима для af (i. ) , где f - выбранная функция. Это особенно полезно для контейнерных структур, таких как массивы, хэш-таблицы, списки и т. Д. Например, для доступа к элементу хеш-таблицы со строковыми ключами можно записать
number: = phone_book ["JILL SMITH"]
"Команды-назначители" - это вспомогательный механизм, позволяющий переосмыслить устоявшиеся и удобные обозначения в рамках объектно- ориентированного программирования. Команды присваивания позволяют схожему с присваиванием синтаксису вызывать процедуры «установщика». Собственно присвоение никогда не может иметь a.x: = v , поскольку это нарушает скрытие информации; вам нужно использовать команду (выбор) установки. Например, класс хеш-таблицы может иметь функцию и функцию
, элемент псевдонима "" (ключ: СТРОКА): ELEMENT [3] - Элемент ключа "key". - ("Получатель" запрос) do. end put (e: ELEMENT; key: STRING) - Вставить элемент ʻe ', связав его с ключом `key'. - (Команда "Setter") do. endЗатем, чтобы вставить элемент, вы должны использовать явный вызов команды setter:
[4] phone_book.put (New_person, "JILL SMITH")
Можно записать это эквивалентно как
[5] phone_book ["JILL SMITH"]: = New_person
(точно так же, как phone_book ["JILL SMITH"] ) синоним для number: = phone_book.item ("JILL SMITH") ), при условии, что объявление элемента теперь начинается (замена для [3]) с псевдонима элемента
»" (ключ: STRING): ELEMENT assign put
Это объявляет put как команду назначения, связанную с item , и в сочетании с псевдонимом скобок делает [5] допустимым и эквивалентным к [4]. записать, используя скобки, как phone_book.item ("JILL SMITH"): = New_person .
note: список аргументов назначителя a должен быть: (возвращаемый тип; весь список аргументов a. )
Лексические и синтаксические свойства
Eiffel не чувствителен к регистру. составляют , maKe и MAKE все обозначают один и тот же идентификатор. Однако см. «Правила стиля» ниже.
Комментарии вводятся с помощью - (два последовательных тире) и расширяются до конца строки.
Точка с запятой в качестве разделителя команд является необязательной. В большинстве случаев точка с запятой просто опускается, за исключением инструкций в строке. Это приводит к меньшему беспорядку в программе. страницу.
Нет вложенности объявлений функций и классов. В результате структуры класса Eiffel проста: некоторые предложения уровня класса (наследование, инвариант) и последовательность объявлений функций, все в том же уровне.
Принято группировать функции в «разделах функций» для большей читабельности, при этом стандартный набор тегов появляется в стандартном порядке, например:
class HASH_TABLE [ELEMENT, KEY ->HASHABLE] наследовать возможность ТАБЛИЦА [ЭЛЕМЕНТ] - Инициализация -. Объявления команд инициализации (процедуры / конструкторы создания). функция - Доступ -. Объявления небулевых запросов о состоянии объекта, например элемент. функция - Отчет о состоянии -. Объявления логических запросов о состоянии объекта, например is_empty. feature - Изменение элемента -. Объявления команд, изменяющих структур, например put. - etc. end
В отличие от сообщества языков программирования с фигурными скобками, Eiffel проводит четкое различие между выражениями и инструкциями. Это соответствует принципу разделения команд и запросов метода Эйфеля.
Соглашения о стилех
Большая часть документации, разработанная Eiffel, использует отличительные соглашения о программном обеспечении внешнего вида. Некоторые из этих соглашений применяются к самому формату кода, а другие - к стандартному типографскому рендерингу кода Эйфеля в форматах и публикациях, где эти соглашения возможны.
Хотя язык нечувствителен кру, стандарты стилей предписывают использование заглавных букв для имен классов ( LIST ) и строчных букв для имен функций ( make ) и начальные капиталы для констант ( Авогадро ). Рекомендуемый стиль также предлагает разделение компонентов многословного определения, как в средняя_температура .
Спецификация Eiffel включает рекомендации по отображению текстов программного обеспечения в форматах набора ключевых слов: выделены слова жирным шрифтом, определяемые стандартные параметры и константы - показаны курсивом , комментарии, операторы и знаки препинания - римским , с текстом программы синим , как в настоящей статье, чтобы отличить его от пояснительного текста. Например, «Привет, мир!» приведенная выше программа будет в документации Eiffel, как показано ниже:
class HELLO_WORLD create make feature make do print ("Hello, world!") end endИнтерфейсы для других инструментов и языков
Eiffel является чисто объектно-ориентированный язык, но предоставляет открытую энергию для взаимодействия суру «внешним» программным продуктом на любом другом языке программирования.
Например, можно программировать операции на уровне машины и операционной системы в C. Eiffel обеспечивает простой интерфейс для подпрограмм C, включая поддержку «встроенного C» (написание тела подпрограммы Eiffel на C, обычно для коротких операций на уровне машины).
Хотя прямая связь между Eiffel и C нет, многие компиляторы Eiffel (Visual Eiffel - одно исключение) выводят исходный код C как промежуточный язык для передачи компилятору C для оптимизации и переносимости. Таким образом, они являются примерами транскомпиляторов. Tecomp компилятора Eiffel может выполнять код Eiffel напрямую (как интерпретатор) без прохождения через промежуточный код C или испускать код C, который будет передан компилятору C для получения оптимизированного нативного кода. В.NET компилятор EiffelStudio напрямую генерирует код CIL (Common Intermediate Language). Компилятор SmartEiffel также может выводить байт-код Java.
Ссылки
Внешние ссылки
- Eiffel Software Веб-сайт компании, представившей Eiffel, был Interactive Software Engineering (ISE).
Eiffel работает в инструментальной среде Java
Корпорация Tower Technologies недавно представила среду для исполнения Java-программ, созданную преимущественно на основе обширного опыта компании в области языка программирования Eiffel, который предшествовал Java и во многом его напоминает. "
Корпорация Tower Technologies недавно представила среду для исполнения Java-программ, созданную преимущественно на основе обширного опыта компании в области языка программирования Eiffel, который предшествовал Java и во многом его напоминает.
"Мы рассматриваем Java как подмножество Eiffel, - заявил Мэдисон Клутье, вице-президент компании Tower по маркетингу. - Опыт работы с языком Eiffel нам очень помог".
Компания Tower разработала свою среду для исполнения Java-программ, не приобретая лицензии на язык Java у компании JavaSoft, подразделения корпорации Sun Microsystems, выдающего эти лицензии.
Используя свой опыт работы с языком Eiffel, который никогда не был широко распространен, компания Tower создала TowerJ - среду для исполнения Java-программ, которая обладает производительностью родного кода и в то же время поддерживает динамические функции Java, позволяя создавать распределенные сетевые приложения.
До сих пор почти все среды Java поддерживали либо статическую компиляцию Java в машинный код (для повышения быстродействия), либо динамическую (just-in-time) компиляцию, позволяющую изменять приложения на этапе выполнения, но снижающую производительность Java.
Компания Tower объединила оба типа компиляции в своей платформе TowerJ для исполнения Java-программ, которую она позиционирует как средство выполнения высокопроизводительных Java-приложений на серверах.
"Мы считаем, что множество компаний намерено создавать новое ПО на Java с целью адаптации унаследованных систем", - говорит Клутье.
Многие полагают, что компания Tower имеет все предпосылки к разработке усовершенствованных сред для исполнения Java-программ.
"Очевидно, что разработчики Tower приобрели немалый опыт по части оптимизации предназначенных для рабочего использования программ, - считает Стив Макклур, аналитик компании International Data Corp. - Представляется, что они имеют технологию компилирования, превосходящую все другие".
По мнению Клутье, имеет смысл создавать серверные приложения на языке программирования Java, поскольку этот язык "по-настоящему" объектно-ориентированный и им намного проще пользоваться, чем С++.
Руководители компании Tower запланировали встречу с представителями JavaSoft, чтобы обсудить условия лицензирования.
Альфа-версия платформы TowerJ появится совсем скоро. Коммерческая версия будет выпускаться с третьего квартала. Лицензия на одну платформу стоит 100 тыс. долл. Компания Tower будет предоставлять компоненты технологии TowerJ по лицензионным соглашениям ОЕМ-производителям. Она сосредотачивается на Java-продуктах для серверов, чтобы реализовать преимущества объектно-ориентированной природы этого языка.