【发布时间】:2009-10-16 20:23:45
【问题描述】:
我对 Oracle 的 PL/SQL(使用 10g)有点陌生,我想知道是否有办法在对象类型中创建私有方法,就像其他语言(Java、 C++、C# 等...)。我知道可以在包中创建私有方法,但我似乎无法为对象类型找到一种方法。我不断收到编译器错误告诉我:
错误:PLS-00539:子程序“FOO”在对象类型主体中声明 并且必须在对象类型规范中定义。
【问题讨论】:
标签: sql oracle plsql oracle10g
我对 Oracle 的 PL/SQL(使用 10g)有点陌生,我想知道是否有办法在对象类型中创建私有方法,就像其他语言(Java、 C++、C# 等...)。我知道可以在包中创建私有方法,但我似乎无法为对象类型找到一种方法。我不断收到编译器错误告诉我:
错误:PLS-00539:子程序“FOO”在对象类型主体中声明 并且必须在对象类型规范中定义。
【问题讨论】:
标签: sql oracle plsql oracle10g
好的,这是一个我非常简单地测试过的潜在解决方案,到目前为止它似乎有效:
创建一个将标记为 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 只帮助我解决构造函数的私有公共代码问题,而不是其他类型的公共代码重构。
...除非有更好的方法来处理对象类型....有人吗?有人吗?
【讨论】:
如果您只需要使用一个子程序中的子程序(函数/过程),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;
【讨论】:
pl/sql 对象中不能有私有方法,可以有多态性和继承,但不能有封装。
当你想要封装(私有方法)时,你可以使用 pl/sql 包。
不可能同时使用这三个。
【讨论】:
这个问题的答案有些困难,但我会尽可能地解释它。首先,这不完全是甲骨文的错; ANSI SQL 定义了一种称为抽象数据类型 (ADT) 的东西,可用于扩展 SQL。我认为 Oracle 很好地遵循了他们的规范。缺少封装的部分原因在于在 SQL 中引用和存储对象的困难。但是,我不会在这里详细介绍,因为我自己并不完全理解。
ADT 可用于为您的数据(无论是在代码中还是在表格中)赋予结构,但它们不能非常复杂。例如,您不能让对象 A 具有对象 B,而对象 B 又具有对象 A。这不能存储在 SQL 表中。您可以通过在 Oracle 中使用 REF 来解决这个问题(不确定其他供应商是如何解决这个问题的),但这成为另一个需要在代码中解决的问题。
我发现 ADT 适用于非常简单的结构和非常简单的成员方法。任何更复杂的东西都需要包。通常,我会编写一个包来实现给定对象类型的成员方法,因为您不能在对象内部调用私有方法。
这是一种痛苦......
【讨论】: