Перегрузка подпрограмм: программирование на PL/SQL

Перегрузка подпрограмм в PL/SQLПрограммы, которые существуют в одной области видимости и имеют одинаковые имена, называются перегруженными. PL/SQL поддерживает перегрузку процедур и функций в разделе объявлений блока (именованного или неименованного), в теле и специфи­кации пакета, а также в объявлении объектного типа. Перегрузка — это очень мощный механизм, и вы должны научиться использовать все его возможности.


Оглавление статьи[Показать]


В следующем простом примере представлены три перегруженные подпрограммы, определенные в разделе объявлений анонимного блока (а следовательно, являющихся локальными):

DECLARE
   /* Первая версия получает параметр DATE. */
   FUNCTION value_ok (date_in IN DATE) RETURN BOOLEAN IS 
   BEGIN
      RETURN date_in <= SYSDATE;
   END;

   /* Вторая версия получает параметр NUMBER. */
   FUNCTION value_ok (number_in IN NUMBER) RETURN BOOLEAN IS 
   BEGIN
      RETURN number_in > 0;
   END;

   /* Третья версия - процедура! */
   PROCEDURE value_ok (number_in IN NUMBER) IS 
   BEGIN
      IF number_in > 0 THEN
         DBMS_OUTPUT.PUT_LINE (number_in || 'is OK!');
      ELSE
         DBMS_OUTPUT.PUT_LINE (number_in || 'is not OK!');
      END IF;
   END;
BEGIN

Когда исполняемое ядро PL/SQL встречает в программе строку вида

IF value_ok (SYSDATE) THEN ...

список фактических параметров сравнивается со списками формальных параметров разных перегруженных модулей. В результате PL/SQL выполняет код того модуля, для которого указанные списки совпадают.

Перегрузку также называют статическим полиморфизмом. Термином «полимор­физм» обозначается возможность языка определять и использовать несколько программ с одинаковыми именами. Если вызываемый вариант определяется на стадии компиляции — это статический полиморфизм. Если решение принимается во время выполнения, то используется термин «динамический полиморфизм»; этот вид полиморфизма обеспечивается наследованием объектных типов.

Перегрузка способна сильно упростить жизнь как вам, так и другим разработчикам. Она консолидирует интерфейсы вызова многих похожих программ в одно имя модуля. «Информационное бремя» перекладывается с разработчика на программный продукт. Например, вам не придется пытаться запомнить шесть разных имен для программ, до­бавляющих значения (даты, строки, флаги, числа и т. д.) в разные коллекции. Вместо этого вы просто сообщаете компилятору, что вы хотите добавить значение, и передаете это значение. PL/SQL и перегруженные программы определяют, что вы хотите сделать, и выполняют необходимые действия за вас.

При создании перегруженных подпрограмм вы потратите больше времени на проекти­рование и реализацию, чем с отдельными, автономными программами. Дополнительное время с лихвой окупится в будущем, потому что и вам, и другим разработчикам будет проще работать с вашим кодом.

 

Преимущества перегрузки

Перегрузка модулей наиболее уместна в следующих ситуациях:

  •  Необходимость поддержки разных типов и наборов данных. Если одно и то же действие применяется к разным видам и наборам данных, перегрузка обеспечивает разные варианты активизации этого действия, а не просто позволяет назвать разные действия одним именем.
  •  Адаптация программы под разные требования. Чтобы ваш код получился по воз­можности универсальным, можно создать несколько его версий с разными вариан­тами вызова. Для этого часто требуется выполнять перегрузку процедур и функций. Хорошим признаком ее уместности служит невостребованный, «лишний» код. К примеру, работая с пакетом DBMS_SQL, вы вызываете функцию DBMS_SQL. EXECUTE, но при выполнении ею DDL-команд возвращаемое значение игнорируется. Если для этой функции создать перегруженную процедуру, DDL-команду можно выполнить следующим образом:

 

BEGIN
   DBMS_SQL.EXECUTE ('CREATE TABLE xyz ...');

Без применения перегрузки вы должны вызвать функцию

DECLARE
   feedback PLS_INTEGER;
BEGIN
   feedback := DBMS_SQL.EXECUTE ('CREATE TABLE xyz ...');

а затем игнорировать переменную feedback.

  •  Необходимость перегрузки по типу, а не по значению. Самая редкая причина для использования перегрузки. В этом случае версия перегруженной программы выбирается в зависимости от типа данных, а не их значения. Хорошим примером такого рода служит функция DBMS_SQL.DEFINE_COLUMN: пакету DBMS_SQL необходимо сообщить тип каждого столбца, выбранного при помощи динамического запроса. Для определения числового столбца можно использовать вызов DBMS_SQL.DEFINE_COLUMN (cur, 1, 1); или вызов
DBMS_SQL.DEFINE_COLUMN (cur, 1, DBMS_UTILITY.GET_TIME);

Конкретное значение роли не играет; мы должны указать «это число», а не привести какое-то конкретное число. Перегрузка обеспечивает элегантное решение этой проблемы. Сейчас мы рассмотрим типичный пример перегрузки, а затем разберемся с ограниче­ниями и рекомендациями по поводу перегрузки.

 

Поддержка разных комбинаций данных

Перегрузка может использоваться для выполнения одного действия с разными комбина­циями данных. Как упоминалось ранее, такая разновидность перегрузки предоставляет не столько общее имя для разных операций, как разные способы вызова одной операции. Возьмем DBMS_OUTPUT.PUT_LINE: эта встроенная функция может использоваться для выво­да значений любых типов данных, которые могут быть явно или неявно преобразованы в строку. Интересно, что в предшествующих версиях Oracle Database (7, 8, 8i, 9i) эта про­цедура была перегружена, но в Oracle Database 10g и выше она не перегружается! Таким образом, если вам потребуется вывести выражение, которое не может быть неявно преоб­разовано в строку, вы не сможете вызвать DBMS_OUTPUT. PUT_LINE и передать это выражение. И что из того, спросите вы? PL/SQL неявно преобразует числа и даты в строки. Какие еще данные приходится выводить? Для начала — как насчет логических значений? Чтобы вывести значение логического выражения, придется написать команду IF сле­дующего вида:

IF l_student_is_registered 
THEN
   DBMS_OUTPUT.PUT_LINE ('TRUE');
ELSE
   DBMS_OUTPUT.PUT_LINE ('FALSE');
END IF;

Глупо, вам не кажется? И разве это не напрасная потеря времени? К счастью, проблема решается очень просто. Постройте на базе DBMS_OUTPUT. PUT_LINE свой пакет с множеством перегрузок. Ниже приводится сильно сокращенная версия такого пакета. При желании вы сможете легко расширить ее, как я делаю в процедуре do.pl. Часть спецификации пакета выглядит так:

PACKAGE do 
IS
   PROCEDURE pl (boolean_in IN BOOLEAN);
   /* Вывод строки. */
   PROCEDURE pl (char_in IN VARCHAR2);
   /* Вывод строки и значения BOOLEAN. */
   PROCEDURE pl (
      char_in	IN	VARCHAR2,
      boolean_in IN	BOOLEAN
   );
   PROCEDURE pl (xml_in IN SYS.XMLType);
END do;

Пакет просто расширяет DBMS_OUTPUT.PUT_LINE. При использовании do.pl я могу вывести логическое значение без написания команды IF, как в следующем примере:

DECLARE
   v_is_valid BOOLEAN :=
      book_info.is_valid_isbn ('5-88888-66');
BEGIN
   do.pl (v_is_valid);

Более того, do.pl можно применять даже к сложным типам данных — таким, как XMLType:

DECLARE
   doc xmltype;
BEGIN
   SELECT ea.report 
   INTO doc
   FROM env_analysis ea 
   WHERE company= 'ACME SILVERPLATING'; 
   do.pl (doc);
END;

Ограничения на использование перегрузки

При выполнении перегрузки необходимо учитывать несколько обстоятельств. Ис­полнительное ядро PL/SQL как на этапе компиляции, так и на этапе запуска должно быть в состоянии отличить друг от друга перегруженные версии подпрограммы; ведь оно не может выполнять две подпрограммы одновременно. Так как все перегруженные версии имеют одинаковые имена, исполнительное ядро не может различать их по име­нам. Для определения того, какую из версий следует выполнить, PL/SQL использует списки параметров и/или сведения о типе программы (процедура или функция). Все это приводит к тому, что на перегруженные программы накладывается ряд ограничений. У перегруженных программ типы хотя бы одного параметра должны принадлежать к разным семействам. Типы данных INTEGER, REAL, DECIMAL, FLOAT и т. д. являются число­выми подтипами, а типы CHAR, VARCHAR2 и LONG — символьными. Если соответствующие параметры отличаются только подтипом, то есть принадлежат к одному супертипу, у PL/SQL не будет достаточной информации для выбора выполняемой программы.

В следующем разделе описаны некоторые усовершенствования Oracle10g (и по­следующих версий), относящиеся к перегрузке числовых типов.

  •  Если у перегруженных программ различаются только имена параметров, то при вызове таких программ должно использоваться связывание по имени. Если имя аргумента не указано, как компилятор сможет различить вызовы двух перегружен­ных программ? И все же ситуаций, в которых связывание по имени отличается по семантическому смыслу от позиционной записи, следует по возможности избегать.
  •  Списки параметров перегруженных программ не должны различаться только ре­жимом использования параметров. Даже если в одной версии для параметра задан режим IN, а в другой — IN OUT, PL/SQL не видит различия между ними.
  •  Все перегруженные программы должны объявляться в одном и том же блоке PL/SQL или иметь одну область видимости (анонимный блок, пакет, отдельная процедура или функция). Нельзя определить одну версию программы в одном блоке (с его об­ластью видимости), а другую — в другом. Вы не можете перегружать две отдельные программы, так как в этом случае одна из них просто заменит другую.
  •  Перегруженные функции не могут различаться только типом возвращаемого значе­ния (указанного в предложении RETURN функции). В момент вызова перегруженной функции компилятор не знает, значение какого типа она будет возвращать. Поэтому он не может определить, какую из версий функции использовать, если все остальные параметры идентичны.

 

Перегрузка числовых типов

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

DECLARE
   PROCEDURE procl (n IN PLS_INTEGER) IS 
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('pls_integer version');
   END;

   PROCEDURE proc1 (n IN NUMBER) IS 
   BEGIN
      DBMS_OUTPUT.PUT_LINE ('number version');
   END;

BEGIN
   proc1 (1.1); 
   proc1 (1);
END;

При попытке выполнить этот код в Oracle9i происходит ошибка:

ORA-06550: line 14, column 4:
PLS-00307: too many declarations of 'PROC1' match this call

Однако при выполнении того же блока в Oracle10g и Oracle11g результат будет таким:

number version 
pls_integer version

Теперь компилятор PL/SQL различает два вызова. Обратите внимание: при передаче не-целочисленного значения вызывается «NUMBER-версия» программы. Это объясняется тем, что PL/SQL при определении соответствия перебирает типы в следующем по­рядке: сначала PLS_INTEGER или BINARY_INTEGER, затем NUMBER, BINARY_FLOAT и наконец, BINARY_DOUBLE. Используется первая перегруженная программа, которая соответствует переданным значениям фактических аргументов.

Хотя эта новая гибкая возможность чрезвычайно удобна, будьте осторожны при ис­пользовании этой крайне неочевидной перегрузки — убедитесь в том, что она работает именно так, как предполагалось. Протестируйте свой код с разными входными данными и проверьте результаты. Помните, что в качестве числового параметра может быть пере­дана строка вида «156.4»; протестируйте и такие данные.

Также можно уточнить тип числовых значений и использовать функции преобразо­вания для явного выбора перегруженной версии. Скажем, если значение 5.0 должно передаваться в формате BINARY_FLOAT, используйте обозначение 5.0f или функцию пре­образования TO_BINARY_FLOAT(5.0).

Вас заинтересует / Intresting for you:

Управление приложениями PL/SQL...
Управление приложениями PL/SQL... 4651 просмотров Stas Belkov Thu, 16 Jul 2020, 06:20:48
Встроенные методы коллекций PL...
Встроенные методы коллекций PL... 14838 просмотров sepia Tue, 29 Oct 2019, 09:54:01
Основы языка PL/SQL: использов...
Основы языка PL/SQL: использов... 4700 просмотров Ирина Светлова Tue, 06 Feb 2018, 14:04:03
Работа с числами в PL/SQL на п...
Работа с числами в PL/SQL на п... 45205 просмотров Antoniy Mon, 28 May 2018, 16:45:11
Войдите чтобы комментировать