Объектно-ориентированное программирование на Фортране связано с большими техническими трудностями в связи с отсутствием в этом языке структур (записей) и динамических объектов. Но поскольку все еще имеется много любителей Фортрана, следует хотя бы кратко рассмотреть основные принципы реализации на Фортране проекта, разработанного по объектно-ориентированной методологии.
Реализация классов с помощью массивов языка Фортран. В Фортране определен всего один структурный тип данных - массив, так что структуры (записи) тоже следует моделировать. Класс представляется как неявный набор массивов, по одному для каждого атрибута класса. Массивы должны иметь одинаковый размер, который должен быть достаточным для включения всех объектов этого класса, которые будут существовать во время выполнения программы, так как Фортран не поддерживает динамического распределения памяти. Значения индекса массивов представляют уникальный идентификатор объекта внутри соответствующего класса. При этом идентификаторы объектов разных классов будут иметь одинаковые значения, так что программист должен сам следить за тем, какому классу принадлежит объект (впрочем реализация механизма наследования, описываемая ниже, может несколько облегчить это). Все массивы одного и того же класса можно объединить в общий блок. Например, память для тысячи окон из рассматриваемого в этом разделе примера может быть организована следующим образом:
COMMON/WINDOW/XMIN,YMIN,XMAX,YMAX,WINDOW REAL XMIN(1000),YMIN(1000),XMAX(1000),YMAX(1000),WINDOW(1000) INTEGER NWINDOW
Программист должен организовать счетчик порожденных объектов данного класса (NWINDOW), и присваивать его значения уникальным идентификаторам вновь порождаемых объектов.
В Фортране нет средств определения новых типов данных, поэтому нет возможности определить тип Length: данные этого типа должны иметь один из определенных в Фортране типов (INTEGER, REAL, COMPLEX, LOGICAL и CHARACTER).
В Стандарте Фортрана ограничена длина идентификаторов, однако большая часть компиляторов поддерживают идентификаторы в 32 и более символов. В примерах фрапгментов фортранных программ предполагается, что допустимы достаточно длинные идентификаторы, которые могут содержать символ подчеркивания ("_"). Если компилятор не поддерживает длинных имен, все идентификаторы из примеров следует заменить более короткими, что, естественно, ухудшит читаемость программ.
Таким образом, в объектной программе на Фортране каждый объект некоторого класса может быть представлен индексом атрибутных массивов этого класса, причем указанный индекс определяет доступ к атрибутам этого объекта:
INTEGER AWINDOW REAL X1 X1 = XMIN(AWINDOW)
При передаче параметров методам объект может передаваться как соответствующий ему индекс атрибутных массивов соответствующего класса:
SOUBROUTINE WINDOW__ADD_TO_SELECTIONS (SELF, SHAPE) INTEGER SELF, SHAPE
Объект, значение которого запрашивается, но не изменяется, может быть в случае необходимости передан как список значений его атрибутов, хотя, конечно, индекс обычно короче, и его передавать удобнее.
FUNCTION CIRCLE__PICK(X0, Y0, RADIUS, X, Y) LOGICAL CIRCLE__PICK REAL X0, Y0, RADIUS, X, Y
Размещение объектов в памяти. Обычно размещение новых объектов производится в заранее определенные массивы. Если требуется динамическое распределение памяти, необходимо самому смоделировать управление кучей в своей программе:
FUNCTION CREATE_WINDOW(X1, Y1, WIDTH, HEIGHT) COMMON/WINDOW/XMIN,YMIN,XMAX,YMAX,WINDOW REAL XMIN(1000),YMIN(1000),XMAX(1000),YMAX(1000),WINDOW(1000) INTEGER NWINDOW INTEGER CREATE_WINDOW REAL X1, Y1, X2, Y2 NWINDOW = NWINDOW + 1 XMIN(NWINDOW) = X1 YMIN(NWINDOW) = Y1 XMAX NWINDOW) = X1 + WIDTH YMAX NWINDOW) = Y1 + HEIGHT CREATE_WINDOW = NWINDOW RETURN END
Реализация наследования. В Фортране нет записей (структур) и тем более вариантных записей. Поэтому наследование при программировании на Фортране можно реализовать либо в виде универсальной записи, либо моделируя вариантные записи.
Первый способ связан с излишними затратами памяти, его следует применять, когда вариантная часть записей сравнительно невелика. При этом подходе организуется универсальная запись, содержащая все атрибуты корневого класса и все атрибуты всех наследующих ему классов. При работе с конкретным классом из рассматриваемой иерархии классов те атрибуты, которые для него не определены, просто игнорируются. Универсальная запись реализуется, как и раньше, через набор массивов одинаковой длины, по одному на каждое поле записи:
COMMON/SHAPE/XMIN,YMIN,XMAX,YMAX,WINDOW,RADIUS,NSHAPE REAL XMIN(1000),YMIN(1000),XMAX(1000),YMAX(1000),WINDOW(1000) REAL RADIUS(1000) INTEGER NSHAPE
Второй способ более экономен. Он состоит в представлении класса как набора подклассов, каждый из которых реализован как самостоятельный класс со своим набором массивов и индексами объектов. Каждый класс представляется парой целых массивов: один из этих массивов содержит код подкласса, другой - индексы объектов в соответствующем массиве подкласса. В следующем примере определен класс ITEM, который имеет подклассы SHAPE (содержит не более 1000 объектов) и GROUP (содержит не более 100 объектов). Общий блок CLASSES определяет целочисленный код для каждого класса.
COMMON/ITEM/ITEM_CLASS,ITEM_ID,NITEM INTEGER ITEM_CLASS(1100),ITEM_ID(1100) INTEGER NITEM/0/ COMMON/CLASSES/GROUP,BOX,CIRCLE INTEGER GROUP/1/,BOX/2/,CIRCLE/3/
Когда создается новый объект, значение индекса должно выбираться как из суперкласса, так и из соответствующего подкласса. Например, следующий код создает новый круг (CIRCLE):
FUNCTION CREATE_CIRCLE(X0,Y0,RADIUS0) COMMON/WINDOW/XMIN,YMIN,XMAX,YMAX,WINDOW сюда следует поместить описания общих блоков ITEM, SHAPE и CLASSES INTEGER CREATE_CIRCLE NSHAPE = NSHAPE + 1 X(NSHAPE) = X0 Y(NSHAPE) = Y0 RADIUS(NSHAPE) = RADIUS0 NITEM = NITEM + 1 ITEM_CLASS(NITEM) = CIRCLE ITEM_ID(NITEM) = NSHAPE CREATE_CIRCLE = NITEM RETURN END
Резолюция методов. Вызовы методов для объектов, класс которых известен во время компиляции, осуществляются как непосредственные вызовы соответствующих фортранных подпрограмм. Остальные объекты должны содержать номер класса (см. выше общий блок CLASSES). Для каждой операции можно составить управляющую подпрограмму, которой в качестве параметров передаются номер класса и индекс объекта. Управляющая подпрограмма содержит оператор перехода по вычислению, который позволяет вызывать требуемый метод:
FUNCTION PICK(CLASS,ID,PX,PY) LOGICAL PICK LOGICAL GROUP_PICK,BOX_PICK,CIRCLE_PICK INTEGER CLASS,ID GOTO(100,200,300) CLASS PICK = .FALSE. RETURN 100 PICK = GROUP_PICK(ID,PX,PY) RETURN 200 PICK = BOX_PICK(ID,PX,PY) RETURN 300 PICK = CIRCLE_PICK(ID,PX,PY) RETURN END
Назад | Содержание | Вперед