Следующая версия
|
Предыдущая версия
|
bb:redbook:211 [2016/03/31 15:34] prospero78 создано |
bb:redbook:211 [2020/10/29 07:08] (текущий) |
| |
==== 1. Понятие о атрибутах методов ==== | ==== 1. Понятие о атрибутах методов ==== |
Атрибуты методов, как и атрибуты параметров, и атрибуты типов — помогают контролировать текст программы как на этапе компиляции, так и на этапе исполнения. Обычно, такие дополнения относят к ООП, но бывает полезно и без ООП где-то ограничить использование процедур-методов, а где-то позволить изменить логику работы программы. Эта глава заканчивает введение в ООП средствами Компонентного Паскаля. [↑] | Атрибуты методов, как и атрибуты параметров, и атрибуты типов — помогают контролировать текст программы как на этапе компиляции, так и на этапе исполнения. Обычно, такие дополнения относят к **ООП**, но бывает полезно и без **ООП** где-то ограничить использование процедур-методов, а где-то позволить изменить логику работы программы. Эта глава заканчивает введение в **ООП** средствами **Компонентного Паскаля**. |
| |
| |
==== 2. Атрибут NEW ==== | ==== 2. Атрибут NEW ==== |
C атрибутом NEW у методов уже приходилось встречаться несколько раз ранее. Как и ключевое слово NEW при создании переменных указательной природы, NEW для методов указывает, что такой метод описывается впервые. Если попытаться описать метод с таким же именем, атрибутами и параметрами (что в сумме называется сигнатура) ещё раз, компилятор посчитает это за ошибку. Не может быть двух методов с одинаковой сигнатурой. Переопределение метода в типе-потомке должно быть без ключевого слова NEW. В сумме, такой подход даёт программисту возможность избежать ошибок при "затирании" ранее определённого метода в базовом типе (либо по забывчивости, либо по невнимательности). | C атрибутом NEW у методов уже приходилось встречаться несколько раз ранее. Как и ключевое слово NEW при создании переменных указательной природы, NEW для методов указывает, что такой метод описывается впервые. Если попытаться описать метод с таким же именем, атрибутами и параметрами (что в сумме называется сигнатура) ещё раз, компилятор посчитает это за ошибку. Не может быть двух методов с одинаковой сигнатурой. Переопределение метода в типе-потомке должно быть без ключевого слова NEW. В сумме, такой подход даёт программисту возможность избежать ошибок при "затирании" ранее определённого метода в базовом типе (либо по забывчивости, либо по невнимательности). |
| |
Пример ниже демонстрирует некоторые возможности абстрактных методов: | Пример ниже демонстрирует некоторые возможности абстрактных методов: |
| 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 — ещё и какие-то дополнительные надписи. [↑] | Как видно из примера, абстрактный метод Log для абстрактного типа tpVector реализуется в типе-потомке tpMyVector и tpMyVector1. Вызовы совершенно одинаковые, а результат работы разный. Для переменной v — это просто значение полей. Для переменной v1 — ещё и какие-то дополнительные надписи. [↑] |
| |
==== 5. Атрибут EXTENSIBLE ==== | ==== 5. Атрибут EXTENSIBLE ==== |
Атрибут EXTENSIBLE для методов, также как и для типов, указывает о том, что метод можно переопределять в типах-потомках. Немного переделав предыдущий пример, можно продемонстрировать как это работает: | Атрибут 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 изменить этот метод — ничего не выйдет. Компилятор такое действие запретит. [↑] | В модуле определено три типа: 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^. Вместо того, чтобы заново переписывать всю логику в методе, было дописано лишь часть кода до и после супер-вызова. Кроме того, часто бывает так, что у программиста нет возможности внести изменения в базовый класс (например, модуль получен в виде уже готового исполняемого машинного кода). [↑] | В примере хорошо видно, что супер-вызов оформлен в виде инструкции v.Log^. Вместо того, чтобы заново переписывать всю логику в методе, было дописано лишь часть кода до и после супер-вызова. Кроме того, часто бывает так, что у программиста нет возможности внести изменения в базовый класс (например, модуль получен в виде уже готового исполняемого машинного кода). [↑] |
| |