Как цепочка вызовов в объектном типе pl / sql функций, возвращающих SELF



Я хочу, чтобы объект oracle возвращал себя и мог связывать эти вызовы. Как мне это сделать?



Я пытался вернуть тот же тип, но он не работает, я также пытался добавить процедуру, которая вызывается функцией, но она тоже не работает. Всегда жалуется на изменение значения элемента width. Похоже, функции не допускают побочных эффектов?, они смоделированы по более математическому принципу функции? Достижимо ли это?. Думаю, я мог бы написать следующее: функция таким образом, он строит новый прямоугольник с самим собой, но это так много работы.



Моя цель состоит в том, чтобы иметь возможность связывать вызовы, такие как jQuery или некоторые классы java (синглет?). Что-то вроде:



r := r.setWidth(0).setWidth(1).setWidth(2);



Конечно, у него было бы больше методов, и он не был бы прямоугольником. Это ошибка:

Error: PLS-00363: expression 'SELF' cannot be used as an assignment target
Line: 18
Text: stWidth(w);


-



CREATE OR REPLACE TYPE rectangle AS OBJECT
(
-- The type has 3 attributes.
length NUMBER,
width NUMBER,
area NUMBER,
-- Define a constructor that has only 2 parameters.
CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
RETURN SELF AS RESULT,
MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle,
MEMBER PROCEDURE stWidth(w NUMBER)
)


-



CREATE OR REPLACE TYPE BODY rectangle AS
CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
RETURN SELF AS RESULT
AS
BEGIN
SELF.length := length;
SELF.width := width;
-- We compute the area rather than accepting it as a parameter.
SELF.area := length * width;
RETURN;
END;
MEMBER PROCEDURE stWidth(w NUMBER) IS
BEGIN
self.width := w;
END;
MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
BEGIN
stWidth(w);

RETURN SELF;
END;
END;


Заранее благодарю.

644   4  

4 ответов:

Вы не можете одновременно изменять объект и назначать ему назначение. Вы уже знаете решение, "построить новый прямоугольник с самим собой". Но это не будет большой работой.

Заменить это:

  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
    BEGIN
        stWidth(w);
        RETURN SELF;
  END;

С этим:

  MEMBER FUNCTION setWidth(w NUMBER) RETURN rectangle IS
      v_rectangle rectangle := self;
    BEGIN
        v_rectangle.width := w;
        RETURN v_rectangle;
  END;

На самом деле вы получили ошибку компиляции. По умолчанию SELF является параметром IN. Вызов stWidth не удался, так как он изменял параметр IN с помощью self.width := w;.

См.: http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjbas.htm#CHDCFEEE

Я всегда первый параметр, передаваемый в метод.

  • В функциях-членах, если SELF не объявлен, его параметр mode по умолчанию-IN.

  • В процедурах-членах, если SELF не объявлен, его режим параметров по умолчанию-IN OUT. Поведение по умолчанию не включает NOCOPY подсказка компилятора.

Извините, я немного опоздал, но другие ответы неверны, по крайней мере, с помощью Oracle 11gR2, и то, что вы пытаетесь достичь, вполне возможно.

Я только недавно наткнулся на эту проблему, и я вижу, что можно окончательно вернуть ссылку на SELF точно так же, как вы пытались, не поддаваясь никаким компромиссам или применяя обходные пути.

Единственное, что нужно, - это переопределить метод, (явно) задав (неявно) SELF параметр as SELF IN OUT rectangle.
SELF является ведущим параметром, который молча передается каждому объектному методу, а для функций определяется как IN (неизменяемый; это то, что предположительно заставляет компилятор жаловаться). Это вроде как установлено во время компиляции, но хорошая часть заключается в том, что когда вызывает метод, вы можете его опустить.
В Примере в конце поста (слегка переписанном с Вашего) мы определяем

MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle

И мы выполняем его, опуская SELF Ссылка:

declare
  r rectangle := rectangle(1,2);
begin

  dbms_output.put_line('width is: ' || r.width);
  dbms_output.put_line('new width is: ' || r.incrementWidth(3).width);

end;
/
Обратите внимание, что есть два предупреждения, о которых следует помнить.

Предупреждение 1

Каждый вызов метода временно создает новую копию объекта.
Но только временно, новый экземпляр недолговечен, как раз между началом и концом метода. Это присуще использованию параметров IN OUT для всех функций или процедур и не является специфичным для типов объектов. Если вы хотите предотвратить такое поведение, вы можете переопределить подпись вашей функции с помощью подсказки NOCOPY:

MEMBER FUNCTION incrementWidth(SELF IN OUT NOCOPY rectangle, w NUMBER) RETURN rectangle

Смотрите ORACLE-BASE - NOCOPY для получения более подробной информации. Обратите внимание, что этоподсказка , которая не гарантирует, что Вы, наконец, используете ту же ссылку на объект, а не вновь созданный объект, поэтому используйте с осторожностью.

Предупреждение 2

Учитывая, что вы подняли этот вопрос, есть вероятность, что у вас есть фон ООП, и вы можете получить сюрприз при попытке вызвать метод без использования возвращенная ссылка выглядит следующим образом

r.incrementWidth(10);

Компилятор вернет ошибку:

PLS-00221: 'INCREMENTWIDTH' is not a procedure or is undefined
Так что же здесь происходит? Ну а так называемый "статический полиморфизм" (то есть выбор метода перегрузки при компиляции) в pl/sql несколько отличается от других языков ООП, поскольку учитывает даже использование возвращаемого типа. Чтобы решить эту проблему, добавьте сопутствующую процедуру с подписью, различие которой заключается только в отсутствии возвращаемого тип:
MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle,
MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER)

Разумно, если вы не хотите дублировать один и тот же код в функции и процедуре, процедура будет внутренне делегировать функцию; и в зависимости от версии Oracle, которую вы используете, вы можете захотеть поиграть с подстановкой кода (см. OCP: More New PL/SQL Features), чтобы достичь той же скорости, что и копируемая реализация (вряд ли вы заметите реальную разницу). Явная "подстановка" указывает на метод по имени, однако она работает и в этом случае где имя метода перегружено.

В приведенном ниже примере вы увидите, что либо функция, либо процедура вызываются альтернативно в зависимости от использования возвращаемого/not_returned параметра.

Итак, наконец-то...

Код, который можно написать, следующий (я не использовал NOCOPY, чтобы не загрязнять соответствующие материалы, но это просто сделать)

CREATE OR REPLACE TYPE rectangle AS OBJECT
(
  length NUMBER,
  width NUMBER,
  CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
    RETURN SELF AS RESULT,
  MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle,
  MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER)
);
/

CREATE OR REPLACE TYPE BODY rectangle AS
  CONSTRUCTOR FUNCTION rectangle(length NUMBER, width NUMBER)
    RETURN SELF AS RESULT
  AS
  BEGIN
    SELF.length := length;
    SELF.width := width;
    RETURN;
  END;
  MEMBER FUNCTION incrementWidth(SELF IN OUT rectangle, w NUMBER) RETURN rectangle IS
    BEGIN
        dbms_output.put_line('...invoking the function with input ' || w);
        width := width + w;
        RETURN SELF;
  END;
  MEMBER PROCEDURE incrementWidth(SELF IN OUT rectangle, w NUMBER) IS
    BEGIN
        PRAGMA INLINE (incrementWidth, 'YES');
        dbms_output.put_line('...invoking the procedure with input ' || w || ', that in turn is...');
        self := incrementWidth(w);
  END;
END;
/

При исполнении...

set serveroutput on
select * from v$version where rownum = 1;

declare
  r rectangle := rectangle(1,2);
begin

  dbms_output.put_line('width is: ' || r.width);

  --this is invoking the "function" version, because we are making use of
  --the returned rectangle object
  dbms_output.put_line('new width is: ' || r.incrementWidth(3).width);

  --the original reference has been updated even without using the NO COPY hint
  dbms_output.put_line('original object has width updated: ' || r.width);

  --this is invoking the "procedure" version, because we are not using the returned object
  r.incrementWidth(3);

  --of course this has finally worked as well
  dbms_output.put_line('again what is the new width like now?: ' || r.width);

end;

/

Вы получаете

BANNER                                                                     
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production  

width is: 2
...invoking the function with input 3
new width is: 5
original object has width updated: 5
...invoking the procedure with input 3, that in turn is...
...invoking the function with input 3
again what is the new width like now?: 8

Вы не можете вернуть из функции-члена. Вы можете создать копию,но я не уверен, почему функция setwidth возвращает объект rectangle. Я знаю, что этот базовый пример, вероятно, взят из некоторых старых документов Oracle, но я бы не стал помещать вычисляемое поле (область) в качестве атрибута. Если он вычисляется, то это должна быть функция-член (или процедура). Причина в том, что если вы не хотите всегда помнить об обновлении области в функциях, которые влияют на область, вы выстрелите себе в ногу. Ваш пример таков нарушено (кроме setWidth, возвращающего self), , так как только конструктор вычисляет область.

set serveroutput on
declare
  r rectangle := rectangle(3,2);
begin
  dbms_output.put_line('Area is: ' || r.area);
  -- change the width
  r.stWidth(4);
  dbms_output.put_line('Area is: ' || r.area);
end;

Вывод:

Area is: 6
Area is: 6

Очевидно, что это неправильно. Я бы сделал что-то вроде:

CREATE OR REPLACE TYPE rectangle AS OBJECT
(
-- The type has 2 attributes.
  len NUMBER,
  width NUMBER,
-- Define a constructor that has only 2 parameters.
  CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER)
    RETURN SELF AS RESULT,
  MEMBER PROCEDURE setLength(l NUMBER),
  MEMBER PROCEDURE setWidth(w NUMBER),
  MEMBER FUNCTION getArea return NUMBER,
  MEMBER PROCEDURE showArea
);


CREATE OR REPLACE TYPE BODY rectangle AS
  CONSTRUCTOR FUNCTION rectangle(len NUMBER, width NUMBER)
    RETURN SELF AS RESULT
  AS
  BEGIN
    SELF.len := len;
    SELF.width := width;
    RETURN;
  END;

  MEMBER PROCEDURE setLength(l NUMBER) IS
  BEGIN
    self.len := l;

  END;
  MEMBER PROCEDURE setWidth(w NUMBER) IS
  BEGIN
    self.width := w;

  END;

  MEMBER FUNCTION getArea return NUMBER IS
  BEGIN
    return self.len * self.width;
  END;

  MEMBER PROCEDURE showArea IS
  BEGIN
    -- Just shows how we calculated area and spits to console
    dbms_output.put_line('Area is: ' || self.getArea || ' (' || self.len || ' * ' || self.width || ')');
  END;
END;

И таким образом вы получите:

set serveroutput on
declare
  r rectangle := rectangle(3,2);
begin
  dbms_output.put_line('Area is: ' || r.getArea);
  -- change the width
  r.setWidth(4);
  dbms_output.put_line('Area is: ' || r.getArea);
end;

Вывод:

Area is: 6
Area is: 12

Я столкнулся с той же проблемой, и так как лучшие ответы не совпадают, мне пришлось экспериментировать самому.

У меня есть следующий код, который не будет работать, несмотря на то, что ответ Антонио говорит:

create or replace type tst as object (
  inner_value varchar2(100),

  constructor function tst return self as result,

  member function update_value(
    self in out nocopy tst, 
    p_value varchar2)
  return tst,

  member procedure print_value(self in out nocopy tst)

) final;
/

create or replace type body tst as
  constructor function tst return self as result
  is
  begin
    self.inner_value := 'DEFAULT';
    return;
  end;

  member function update_value(
    self in out nocopy tst, 
    p_value varchar2)
  return tst
  is
  begin
    self.inner_value := p_value;
    return self;
  end;

  member procedure print_value(self in out nocopy tst)
  is
  begin
    dbms_output.put_line(self.inner_value);
  end;

end;
/

Следующий код не будет выполняться независимо от того, определен ли параметр self с помощью nocopy или нет.

set serveroutput on;
begin
  tst().update_value('TEST').print_value;
end;
/

Если print_value переопределено как member procedure print_value(self in tst), то tst().print_value; становится допустимым утверждением.

Конструктор ѕневозможно быть соединены, если параметр self является in out или in out nocopy, независимо от того, возвращаете ли вы копию или self, вы всегда получите PLS-00363. Единственный способ выполнить вложенные вызовы конструктора-использовать параметры self in везде и сделать копию, что разочаровывает:

create or replace type tst as object (
  inner_value varchar2(100),

  constructor function tst return self as result,

  member function update_value(
    self in tst, 
    p_value varchar2)
  return tst,

  member procedure print_value(self in tst)

) final;
/

create or replace type body tst as
  constructor function tst return self as result
  is
  begin
    self.inner_value := 'DEFAULT';
    return;
  end;

  member function update_value(
    self in tst, 
    p_value varchar2)
  return tst
  is
    a_copy tst;
  begin
    a_copy := tst;
    a_copy.inner_value := p_value;
    return a_copy;
  end;

  member procedure print_value(self in tst)
  is
  begin
    dbms_output.put_line(self.inner_value);
  end;

end;
/

Тогда это выводит TEST как и ожидалось:

set serveroutput on;
begin
  tst().update_value('TEST').print_value;
end;
/

Если вы можете разделить оператор на t := tst(); t.update_value('TEST').print_value;, то он работает даже с in out nocopy self параметры.

Comments

    Ничего не найдено.