PL/SQL 私有对象方法

Posted

技术标签:

【中文标题】PL/SQL 私有对象方法【英文标题】:PL/SQL private object method 【发布时间】:2009-10-16 20:23:45 【问题描述】:

我对 Oracle 的 PL/SQL(使用 10g)有点陌生,我想知道是否有办法在对象类型中创建私有方法,就像其他语言(Java、 C++、C# 等...)。我知道可以在包中创建私有方法,但我似乎无法为对象类型找到一种方法。我不断收到编译器错误告诉我:

错误:PLS-00539:子程序“FOO”在对象类型主体中声明
并且必须在对象类型规范中定义。

【问题讨论】:

【参考方案1】:

好的,这是一个我非常简单地测试过的潜在解决方案,到目前为止它似乎有效:

创建一个将标记为 NOT FINAL 和 NOT INSTANTIABLE 的父对象类型,然后将所有私有代码放入其中。私有方法不会是真正私有的,但是将它们置于非最终且不可实例化的类型中会阻止它们被调用。在可实例化子类型中,通过 SELF 引用超类型中的“私有”方法。 示例:


create or replace type PrivateFoo under SuperFoo
(

  member procedure setUpCommonFoo
) NOT INSTANTIABLE NOT FINAL;

create or replace type body PrivateFoo is
  -- Member procedures and functions
  member procedure setUpCommonFoo is
        begin
            SELF.someAttrib:='Some Common Default Value';
        end;
end;

create or replace type Foo under PrivateFoo
(
    CONSTRUCTOR FUNCTION Foo RETURN SELF AS RESULT,
    CONSTRUCTOR FUNCTION Foo(fkey FooKey) RETURN SELF AS RESULT -- assume fkey is defined in SuperFoo, and FooKey type is defined somewhere else ;)
)

create or replace type body Foo is
    --no-arg Constructor For basic Foo set up.
    CONSTRUCTOR FUNCTION PartyConvertor RETURN SELF AS RESULT AS
    BEGIN
        self.setUpCommonFoo;
        RETURN;
    END;
        --alt constructor for other situations...
    CONSTRUCTOR FUNCTION PartyConvertor(fkey FooKey) RETURN SELF AS RESULT AS
    BEGIN
        self.setUpCommonFoo;
                SELF.rarelyUsedAttrib:='Special Value!'; --just assume that someAttrib and rarelyUsedAttrib actually exist ;)
        self.fkey := fkey;
        RETURN;
    END;
        --Other Members go here...
end;

现在我不得不承认,我不太喜欢这种模式。这似乎很尴尬和笨拙。我可能会尽可能地避免使用对象类型并坚持使用包(或非常简单的对象类型)。 package-as-fatory 只帮助我解决构造函数的私有公共代码问题,而不是其他类型的公共代码重构。

...除非有更好的方法来处理对象类型....有人吗?有人吗?

【讨论】:

【参考方案2】:

如果您只需要使用一个子程序中的子程序(函数/过程),PL/SQL 确实允许您在声明块中将一个子程序嵌套在另一个子程序中。

它没有私有方法或函数那么理想,但在创建继承层次结构之前可能值得一试。

create or replace type body some_t
as

member function foo
    return varchar2
    as
        function some_private_foo
            return varchar2
            as
            begin
                return 'Foo!';
            end some_private_foo;
    begin
        return some_private_foo();
    end foo;

end;

如果您使用的是 Oracle 12,那么您很幸运。您可以使用 ACCESSIBLE BY 子句创建一个只有您的类型才能对其进行编码的包。在下面的示例中,PL/SQL 编译器将只允许 FOO_T 中的代码引用 FOO_PRIVATE_PKG。

CREATE OR REPLACE package foo_private_pkg 
accessible by ( foo_t )
as

function some_private_foo ( object_in in out nocopy foo_t )
    return varchar2;
end;

【讨论】:

【参考方案3】:

pl/sql 对象中不能有私有方法,可以有多态性和继承,但不能有封装。

当你想要封装(私有方法)时,你可以使用 pl/sql 包。

不可能同时使用这三个。

【讨论】:

好的,这会让事情变得困难。我有一个具有多个构造函数的对象类型,它们并不都采用相同的参数,但它们都有 4-5 行通用代码。我已经发现 PL/SQL 不允许构造函数链接,所以我想我可以将通用设置代码放入私有方法中。对于这类问题有什么好的 PL/SQL 解决方案? 我不知道。也许您可以创建一个带有返回对象的重载函数的包?我现在没有可用的 Oracle,所以我无法尝试。将其视为替代工厂。 其实……这又提出了一个好点:如果我在一个对象类型的几个方法中找到共同的代码,我该如何重构它?我不能将它分解为私有方法,有没有办法将它分解为一个公共方法,该方法不能被它所在的类之外的任何东西调用?还是我们只需要将其记录为“内部管理方法 - 放手!!”并希望没有人在原来的类之外称它?【参考方案4】:

这个问题的答案有些困难,但我会尽可能地解释它。首先,这不完全是甲骨文的错; ANSI SQL 定义了一种称为抽象数据类型 (ADT) 的东西,可用于扩展 SQL。我认为 Oracle 很好地遵循了他们的规范。缺少封装的部分原因在于在 SQL 中引用和存储对象的困难。但是,我不会在这里详细介绍,因为我自己并不完全理解。

ADT 可用于为您的数据(无论是在代码中还是在表格中)赋予结构,但它们不能非常复杂。例如,您不能让对象 A 具有对象 B,而对象 B 又具有对象 A。这不能存储在 SQL 表中。您可以通过在 Oracle 中使用 REF 来解决这个问题(不确定其他供应商是如何解决这个问题的),但这成为另一个需要在代码中解决的问题。

我发现 ADT 适用于非常简单的结构和非常简单的成员方法。任何更复杂的东西都需要包。通常,我会编写一个包来实现给定对象类型的成员方法,因为您不能在对象内部调用私有方法。

这是一种痛苦......

【讨论】:

以上是关于PL/SQL 私有对象方法的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL包(package)操作实例讲解

如何调用 Oracle PL/SQL 对象超级方法

如何在 Package Body oracle pl/sql 中添加第二个私有函数?

SQL记录-PLSQL包

PL/SQL练习游标cursor :oracle 在执行sql语句时,为sql语句所分配的一个私有的内存区域

PL-SQL 包的创建和应用