Шаг 99 - PL/SQL - Пакеты - Переопределение, функций и процедур

Думаю, что наверняка кто-то из вас знаком с языками высокого уровня и с принципами объектно ориентированного программирования. Так вот. При определении пакетов можно применять очень интересный фокус под названием "переопределение функций и процедур" (overloaded). Например в C++ или C#, на котором я сейчас в основном пишу клиентские части для Oracle сервера, есть понятие "перегруженные функции". Когда в среде Visual Studio.NET, вы открываете скобку после определяющей сигнатуры функции то видите, то что изображено на рисунке:

99_1.gif (3716 b)

Цифра 7 означает, что функция имеет семь перегруженных сигнатур. При выборе конкретной из них, срабатывает именно та, чья сигнатура определена в данный момент. Процедуры и функции внутри модуля (пакета) так же могут быть переопределены. То есть можно определить несколько процедур или функций с одним и тем же именем, но с разными параметрами. В PL/SQL - есть смысл применять данное свойство пакета, применительно к обработке объектов различных типов. Здесь как раз и просматривается, некое подобие или попытка привести PL/SQL - к объектно-ориентированной модели, применяя основные принципы ООП. Давайте практически попробуем это реализовать. Запишем такой заголовок пакета:

CREATE OR REPLACE PACKAGE TST_OVERLOAD IS

	PROCEDURE Out_Screen(TOSC IN VARCHAR2);
	
	FUNCTION Add_One_Num(NM IN NUMBER, BM IN NUMBER) RETURN NUMBER;
	
	FUNCTION Add_One_Num(A IN INTEGER) RETURN NUMBER;

	FUNCTION Add_One_Num(M IN NUMBER, K IN VARCHAR2) RETURN VARCHAR;
	
END TST_OVERLOAD;
/

Получаем после компиляции:

SQL> CREATE OR REPLACE PACKAGE TST_OVERLOAD IS
  2  
  3   PROCEDURE Out_Screen(TOSC IN VARCHAR2);
  4   
  5   FUNCTION Add_One_Num(NM IN NUMBER, BM IN NUMBER) RETURN NUMBER;
  6   
  7   FUNCTION Add_One_Num(A IN INTEGER) RETURN NUMBER;
  8  
  9   FUNCTION Add_One_Num(M IN NUMBER, K IN VARCHAR2) RETURN VARCHAR;
 10   
 11  END TST_OVERLOAD;
 12  /

Пакет создан.

В данном случае функция Add_One_Num имеет три типа инициализации. К стати обратите внимание на следующий интересный факт:

SELECT OBJECT_NAME, OBJECT_TYPE, STATUS 
FROM USER_OBJECTS
WHERE OBJECT_TYPE = 'PACKAGE'
/

Получаем:

SQL> SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
  2  FROM USER_OBJECTS
  3  WHERE OBJECT_TYPE = 'PACKAGE'
  4  /

OBJECT_NAME                OBJECT_TYPE        STATUS
-------------------------- ------------------ -------
TST_OVERLOAD               PACKAGE            VALID

Оп! А поле STATUS показывает VALID! Но тела то, еще вообще нет! Где логика? Странный факт, но оставим это на совести Лари Элисона! :) Создаем тело пакета, чтобы успокоить совесть! Запишем:

-- PACKAGE BODY test_pkg --
CREATE OR REPLACE PACKAGE BODY TST_OVERLOAD IS

-- PROCEDURE Out_Screen --
PROCEDURE Out_Screen(TOSC IN VARCHAR2)
IS

BEGIN

 	DBMS_OUTPUT.enable;
    DBMS_OUTPUT.put_line(TOSC);

END Out_Screen;

-- FUNCTION Add_One_Num -- One
FUNCTION Add_One_Num(NM IN NUMBER, BM IN NUMBER) RETURN NUMBER
IS

BEGIN

 RETURN (NM + BM);

END Add_One_Num;

-- FUNCTION Add_One_Num -- Two
FUNCTION Add_One_Num(A IN INTEGER) RETURN NUMBER
IS

BEGIN

 RETURN (A + 20);

END Add_One_Num;

-- FUNCTION Add_One_Num --
FUNCTION Add_One_Num(M IN NUMBER, K IN VARCHAR2) RETURN VARCHAR
IS

BEGIN

 RETURN (TO_CHAR(M + TO_NUMBER(K)));

END Add_One_Num;

END TST_OVERLOAD;
/

После компиляции получаем:

SQL> CREATE OR REPLACE PACKAGE BODY TST_OVERLOAD IS
  2  
  3  -- PROCEDURE Out_Screen --
  4  PROCEDURE Out_Screen(TOSC IN VARCHAR2)
  5  IS
  6  
  7  BEGIN
  8  
  9    DBMS_OUTPUT.enable;
 10      DBMS_OUTPUT.put_line(TOSC);
 11  
 12  END Out_Screen;
 13  
 14  -- FUNCTION Add_One_Num -- One
 15  FUNCTION Add_One_Num(NM IN NUMBER, BM IN NUMBER) RETURN NUMBER
 16  IS
 17  
 18  BEGIN
 19  
 20   RETURN (NM + BM);
 21  
 22  END Add_One_Num;
 23  
 24  -- FUNCTION Add_One_Num -- Two
 25  FUNCTION Add_One_Num(A IN INTEGER) RETURN NUMBER
 26  IS
 27  
 28  BEGIN
 29  
 30   RETURN (A + 20);
 31  
 32  END Add_One_Num;
 33  
 34  -- FUNCTION Add_One_Num --
 35  FUNCTION Add_One_Num(M IN NUMBER, K IN VARCHAR2) RETURN VARCHAR
 36  IS
 37  
 38  BEGIN
 39  
 40   RETURN (TO_CHAR(M + TO_NUMBER(K)));
 41  
 42  END Add_One_Num;
 43  
 44  END TST_OVERLOAD;
 45  /

Тело пакета создано.

Чему мы и радуемся! Наконец получили пакет с переопределенными функциями. Теперь можно проверить как ведет себя наша немного комическая функция Add_One_Num! :) Хотя это собственно не важно, главное чтобы вы хорошо усвоили, как это все работает. Итак, запишем анонимный блок:

SET SERVEROUTPUT ON

DECLARE 

BEGIN

TST_OVERLOAD.Out_Screen(TO_CHAR( TST_OVERLOAD.Add_One_Num(TST_OVERLOAD.Add_One_Num(6), 
	TO_CHAR(TST_OVERLOAD.Add_One_Num(4, '7')))));
TST_OVERLOAD.Out_Screen(TO_CHAR(TST_OVERLOAD.Add_One_Num(2,2)));
	
END;
/

Получаем:

SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE 
  2  
  3  BEGIN
  4  
  5  TST_OVERLOAD.Out_Screen(TO_CHAR( TST_OVERLOAD.Add_One_Num(TST_OVERLOAD.Add_One_Num(6),
  6  	TO_CHAR(TST_OVERLOAD.Add_One_Num(4, '7')))));
  7  TST_OVERLOAD.Out_Screen(TO_CHAR(TST_OVERLOAD.Add_One_Num(2,2)));
  8   
  9  END;
 10  /
37
4

Процедура PL/SQL успешно завершена.

И снова арифметика за первый класс торжествует 2 плюс 2 равно 4! И с этим не поспорить. Но, имеются некоторые ограничения! Что поделать - такова жизнь!

  1. Нельзя переопределить процедуру или функцию, если их сигнатуры отличаются только именами или видами.
    Например:
    PROCEDURE Add_One_Num(NM IN NUMBER);
    PROCEDURE Add_One_Num(NM OUT NUMBER);
    
    или
    PROCEDURE Add_One_Num(A IN NUMBER);
    PROCEDURE Add_One_Num(B IN NUMBER);
    
  2. Нельзя переопределить процедуру или функцию, если их сигнатуры отличаются лишь типами возвращаемых ими данных.
    Например:
    FUNCTION Add_One_Num RETURN DATE;
    FUNCTION Add_One_Num RETURN VARCHAR;
    
  3. Типы параметров процедур или функций должны принадлежать различным семействам типов. Например, CHAR и VARCHAR это одно и тоже по этому нельзя переопределять процедуры вот так:
    PROCEDURE Add_One_Num(NM IN CHAR);
    PROCEDURE Add_One_Num(NM IN VARCHAR);
    

Но самое интересное, что на этапе компиляции вы не получите сообщений об ошибке, а вот при вызове такой функции возникнет ошибка PLS-307: too many declarations of "имя процедуры функции" match this call (этому вызову соответствует слишком много объявлений ... бла бла бла ) так что, будьте внимательны, переопределяя функции и процедуры в пакетах. :+)


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Летучий Сергей.