next up previous contents
След.: Наследование Выше: Объектно-ориентированное программирование Пред.: Объектно-ориентированное программирование   Содержание

Введение

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

Концепция объектно-ориентированного программирования (ООП) опирается на понятия объекта -- структуры данных, содержащей информацию, описывающую тот или иной объект «реального мира» и его поведение. Иногда эта структура данных называется классом, а объект -- это переменная данного типа. Предполагается, что объект «знает», какие операции с ним возможны, и содержит соответствующий код: процедуры, наываемые методами. Больше того, концепция ООП не рекомендует прямой доступ к данным, содержащимся в объекте, а только посредством методов. Сокрытие деталей реализации внутри класса носит название абстракции, а объединение данных и кода -- инкапсуляцией. Например, класс «матрица», скорее всего, будет содержать целочисленные поля для размеров, массив для элементов, методы для вычисления ранга, а также для транспонирования и матричной алгебры. Абстракция означает, что пользователь работает с матрицей и не обязан знать о внутреннем устройстве класса, а инкапсуляция -- сокрытие реализации алгоритмов работы с матрицей внутри класса. Так, в процедурной модели вызывается процедура вычисления ранга, который передается аргумент -- матрица, а в объектной -- матрица вычисляет и возвращает свой ранг. Одним из преимуществ является возможность, например, не вычислять ранг при каждом обращении к нему -- он может, будучи вычислен, храниться в объекте до изменения элементов матрицы.

Создание объекта некоторого класса может быть сложным процессом: требуется разместить размещаемые данные, присвоить начальные значения полям, возможно, загрузить информацию из файла и т.п. Процедура, вызываемая при создании объекта, называется конструктором. Автоматически вызываемых конструкторов в Фортране не предусмотрено, но их можно эмулировать -- см. ниже. Уничтожение объекта тоже может быть сложным процессом. Процедура, уничтожающая объект (освобождающая память, разрывающая связи и т.п.) называется деструктором. В терминологии Фортрана -- завершающая процедура.

Еще одной отличительной чертой ООП является наследование, то есть описание класса на базе уже существующего класса. Это может быть весьма удобно, если объекты реального мира образуют иерархическую структуру. Например, класс квадратных матриц является подклассом матриц, поэтому разумно описывать его на базе уже существующего класса. Алгоритмы матричной алгебры, например, можно унаследовать от общего класса, а можно реализовать заново, более эффективно. При этом в теле новой процедуры можно вызвать процедуру, определенную в расширяемом типе, «расширив» ее действие. Очевидно, добавятся методы для вычисления определителя, следа, спектра. В терминологии Фортрана класс квадратных матриц расширяет класс матриц. Классы симметричных и ортогональных матриц расширяют класс квадратных матриц. Образуется древовидная структура классов.

В ряде случаев имеет смысл начинать иерархию классов (возможно, в рамках более широкой иерархии) с общего класса, объекты которого создавать не предполагается, поскольку они не соотносятся с объектами реального мира, а с некими идеальными понятиями. Такие классы называются абстрактными. Они могут содержать отложенные процедуры, описанные, но не реализованные. Реализовывать соответствующее поведение должны классы, расширяющие данный абстрактный класс. Например, можно начать иерархию классов с абстрактного класса «многомерный числовой объект», для которого реализована сумма и описано, но не реализовано умножение. Наследниками будут класс матриц, векторов и тензоров, реализующие каждый свое умножение: матричное, векторное и тензорное.

Наконец, понятие полиморфизма (статического или динамического) означает возможность процедур работать с данными различных типов. В первую очередь это связано с наследованием: ведь симметричная матрица является квадратной, а также просто матрицей, поэтому процедура, принимающая аргумент типа «матрица», должна принять и аргумент типа «симметричная матрица», причем даже в том случае, если этот тип (класс) не существует на момент разработки процедуры. Статический полиморфизм подразумевает, что тип аргументов процедур проверяется на этапе компиляции. Его реализует (без связи с ООП) механизм родовых интерфейсов (§11.4.3). В контексте ООП можно определить функцию sin, и без того полиморфную (аргументы могут иметь любой числовой тип), для квадратных матриц. Впрочем, для симметричных матриц придется создавать новую специфическую функцию либо явно преобразовывать аргумент к типу «квадратная матрица», что неудобно. В сочетании с наследованием требуется именно динамический полиморфизм, который означает проверку типов на этапе выполнения программы. Средства полиморфизма означают, что аргумент процедуры может иметь не какой-либо конкретный тип, а «тип или расширяющие его типы» или «любой тип», причем есть средства для выяснения типа аргумента. Средства полиморфизма позволяют создать, например, функцию для вычисления синуса матрицы без разработки особой функции для симметричных или ортогональных матриц (хотя при желании это, конечно, возможно), причем функция, будучи откомпилирована, будет работать с новыми классами, расширяющими класс матриц и его потомков. Помимо аргументов процедур, полиморфными могут быть также указатели и размещаемые данные: их тип определяется при связывании указателя с целью или при размещении.

Типичной для ООП, хотя и не присущей исключительно ООП чертой (то же можно сказать и про все вышеперечисленные понятия), является перегрузка операций. Это в некотором роде следствие абстракции и инкапсуляции: раз пользователь работает с объектами класса матриц как реальными матрицами, логично обозначать операции матричной алгебры традиционными символами. Так, можно «перегрузить» (не вполне удачный перевод слова overload, лучше «расширить») операцию + так, чтобы она могла работать с операндами типа «матрица», реализуя сложение матриц. Для операции умножения матриц подойдет * (порядок операндов важен при реализации, поэтому некоммутативную оерацию вполне можно реализовать). Символ / можно перегрузить для решения систем линейных уравнений, и т.п. Можно создать также и новые операции. Например, символ * можно перегрузить для поэлементного умножения, а для матричного -- ввести новую операцию. Возможна также перегрузка присваивания и операций ввода и вывода для объектов.

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

Некоторые черты объектно-ориентированной парадигмы присутствовали в Fortran-90: концепция модулей позволяла объединять данные и относящийся к ним код, причем атрибуты PRIVATE и PROTECTED позволяли скрывать реализацию от пользователя; новые модули имели возможность использовать ранее созданные, что является наследованием; наконец, родовые интерфейсы реализовывали концепцию полиморфизма. Подробно ООП на Fortran-90 описано в [8]. Разумеется, эти средства сохранились в более поздних версиях Стандарта, но в Fortran-2003 введена полная поддержка ООП на базе производных типов (§ 3.6). Производный тип, содержащий описание скрытых и открытых данных, а также процедур, является классом, а переменные этого типа -- объектами.



Ilya A. Chernov 2012-12-19
X