Атрибуты методов, как и атрибуты параметров, и атрибуты типов — помогают контролировать текст программы как на этапе компиляции, так и на этапе исполнения. Обычно, такие дополнения относят к ООП, но бывает полезно и без ООП где-то ограничить использование процедур-методов, а где-то позволить изменить логику работы программы. Эта глава заканчивает введение в ООП средствами Компонентного Паскаля.
C атрибутом NEW у методов уже приходилось встречаться несколько раз ранее. Как и ключевое слово NEW при создании переменных указательной природы, NEW для методов указывает, что такой метод описывается впервые. Если попытаться описать метод с таким же именем, атрибутами и параметрами (что в сумме называется сигнатура) ещё раз, компилятор посчитает это за ошибку. Не может быть двух методов с одинаковой сигнатурой. Переопределение метода в типе-потомке должно быть без ключевого слова NEW. В сумме, такой подход даёт программисту возможность избежать ошибок при «затирании» ранее определённого метода в базовом типе (либо по забывчивости, либо по невнимательности).
Пример использования ключевого слова NEW не приводится, так как встречался ранее. [↑]
Как и в атрибутах типов, в атрибутах методов ABSTRACT указывает на то, что данный метод нельзя вызвать непосредственно. Его необходимо реализовать в типе-потомке. Кроме того, абстрактный метод должен быть в составе абстрактного типа (который тоже нельзя использовать непосредственно). Если есть необходимость переопределения метода в типе-потомке как абстрактного, то в базовом типе этот же метод тоже должен быть абстрактным. В случае реализации, само собой, метод как абстрактный уже не помечается. Также важно помнить, что абстрактный метод не содержит логики работы — только само определение. В типе-потомке, если он наследует абстрактный базовый тип, должны быть переопределены все абстрактные типы, иначе компиляция будет невозможна. Ну и, конечно, если метод, или базовый тип объявлены абстрактными, они должны быть помечены как экспортируемые. А иначе как к ним добраться из другого типа?
Пример ниже демонстрирует некоторые возможности абстрактных методов:
MODULE Test_abs_method; IMPORT Log; TYPE tVector = POINTER TO ABSTRACT RECORD x*: REAL; END; tMyVector = POINTER TO RECORD (tVector) z*: REAL; END; tMyVector1 = POINTER TO RECORD (tVector) z*: REAL; END; VAR v: tMyVector; v1: tMyVector1; PROCEDURE (v: tVector)Log*, NEW, ABSTRACT; PROCEDURE (v: tMyVector)Log*(); BEGIN Log.Real(v.z); Log.Real(v.z); Log.Ln END Log; PROCEDURE (v: tMyVector1)Log*(); BEGIN Log.String("Привет, мир!"); Log.Ln; Log.Real(v.z); Log.Real(v.z); Log.Ln; Log.String("-------------"); Log.Ln; END Log; PROCEDURE Start*; BEGIN v.Log; v1.Log END Start; BEGIN NEW (v); NEW (v1); END Test_abs_method.
(!)Test_abs_method.Start компилируется "Test_abs_method" 236 8 старый модуль Test_abs_method выгружен 0.0 0.0 Привет, мир! 0.0 0.0 -------------
Как видно из примера, абстрактный метод Log для абстрактного типа tpVector реализуется в типе-потомке tpMyVector и tpMyVector1. Вызовы совершенно одинаковые, а результат работы разный. Для переменной v — это просто значение полей. Для переменной v1 — ещё и какие-то дополнительные надписи. [↑]
Этот атрибут подобен атрибуту ABSTRACT. Ключевое слово EMPTY дописывается в одну строку после объявления процедуры:
PROCEDURE (v: tpVector)Log*, NEW, EMPTY;
Отличие EMPTY(«пустой») от ABSTRACT в том, что такой метод вызвать можно. Правда, вызов никакого эффекта иметь не будет. Также нельзя забывать, что впервые описанный метод должен сопровождаться атрибутом NEW. [↑]
Атрибут EXTENSIBLE для методов, также как и для типов, указывает о том, что метод можно переопределять в типах-потомках. Немного переделав предыдущий пример, можно продемонстрировать как это работает:
MODULE Test_ext_method; IMPORT Log; TYPE tVector = POINTER TO ABSTRACT RECORD x*: REAL; END; tMyVector = POINTER TO EXTENSIBLE RECORD (tVector) y*: REAL; END; tMyVector1 = POINTER TO RECORD (tMyVector) z*: REAL; END; VAR v: tMyVector; v1: tMyVector1; PROCEDURE (v: tVector)Log*, NEW, ABSTRACT; PROCEDURE (v: tMyVector)Log*(), EXTENSIBLE; BEGIN Log.Real(v.x); Log.Real(v.y); Log.Ln END Log; PROCEDURE (v: tMyVector1)Log*(); BEGIN Log.String("Привет, мир!"); Log.Ln; Log.Real(v.x);Log.Real(v.y); Log.Real(v.z); Log.Ln; Log.String("-------------"); Log.Ln; END Log; PROCEDURE Start*; BEGIN v.Log; v1.Log END Start; BEGIN NEW (v); NEW (v1); END Test_ext_method.
(!)Test_ext_method.Start компилируется "Test_ext_method" 248 8 старый модуль Test_ext_method выгружен 0.0 0.0 Привет, мир! 0.0 0.0 0.0 -------------
В модуле определено три типа: tpVector, tpMyVector и tpMyVector1. Причём, все они являются друг другу базовыми. В каждом типе добавляется по одному полю, и переопределяется метод Log (кроме, разумеется самого «древнего» типа, который полностью абстрактный — tpVector). В первом типе метод Log — абстрактный, с ним вообще нельзя работать. Во втором случае — уже работает, и его можно переопределять. А в третьем случае, метод не помечен как EXTENSIBLE, а это значит, что если вдруг возникнет необходимость, например, в типе tpMyVector2 изменить этот метод — ничего не выйдет. Компилятор такое действие запретит. [↑]
Ничего страшного в понятии супер-вызов нет. Супермены к нему никакого отношения не имеют. Речь идёт о вызове из типа-потомка метода, который определён в базовом типе. При этом в типе-потомке этот же метод переопределён. Если это дело обстоит именно таким образом, то как обратиться к оригинальному методу? Вот для этого и служит супер-вызов. Такие вызовы могут потребоваться, когда к методу базового типа нужно добавить совсем немного функционала.
Пример использования супер-вызова приведён ниже:
MODULE Test_super_method; IMPORT Log; TYPE tVector = POINTER TO EXTENSIBLE RECORD x*: REAL; END; tMyVector = POINTER TO RECORD (tVector) y*: REAL; END; VAR v: tVector; v1: tMyVector; PROCEDURE (v: tVector)Log*(), NEW, EXTENSIBLE; BEGIN Log.Real(v.x); Log.Ln END Log; PROCEDURE (v: tMyVector)Log*(); BEGIN Log.String("Привет, мир!"); Log.Ln; Log.Real(v.y);v.Log^; Log.String("-------------"); Log.Ln; END Log; PROCEDURE Start*; BEGIN v.Log; v1.Log END Start; BEGIN NEW (v); NEW (v1); END Test_super_method.
(!)Test_super_method.Start компилируется "Test_super_method" 200 8 старый модуль Test_super_method выгружен 0.0 Привет, мир! 0.0 0.0 -------------
В примере хорошо видно, что супер-вызов оформлен в виде инструкции v.Log^. Вместо того, чтобы заново переписывать всю логику в методе, было дописано лишь часть кода до и после супер-вызова. Кроме того, часто бывает так, что у программиста нет возможности внести изменения в базовый класс (например, модуль получен в виде уже готового исполняемого машинного кода). [↑]
В этой главе были рассмотрены атрибуты методов для реализации ООП в Компонентном Паскале. Вместе с предыдущими главами, это уже достаточная база для реализации ООП в программах на Компонентном Паскале. Не раскрыты очень много вопросов как грамотно использовать ООП, чтобы компонентное программирование было устойчивым и понятным. Но в целом, Компонентный Паскаль и БлэкБокс как надстройка гарантируют правильность вызовов методов и обращения к полям-свойствам в силу закреплённого стандартом контроля межмодульного взаимодействия. Обрушить программную систему на Компонентном Паскале будет не просто. [↑]
[ ← Назад ] [ Вверх ↑ ] [ Далее → ]