2009-10-16 8 views
2

Je suis un peu nouveau dans PL/SQL d'Oracle (en utilisant 10g), je me demandais s'il était possible de créer une méthode privée dans un type d'objet, comme c'est souvent le cas pour les méthodes privées dans d'autres langues (Java, C++, C#, etc ...). I sais il est possible de faire des méthodes privées dans les paquets, mais je n'arrive pas à trouver un moyen de le faire pour les types d'objets. Je continue à obtenir des erreurs du compilateur me disant:Méthode d'objet privé PL/SQL

Error: PLS-00539: subprogram 'FOO' is declared in an object type body 
and must be defined in the object type specification.

Répondre

1

Vous ne pouvez pas avoir des méthodes privées dans les objets pl/sql, vous pouvez avoir le polymorphisme et l'héritage, mais pas l'encapsulation.

Lorsque vous souhaitez l'encapsulation (méthodes privées), vous pouvez utiliser des packages pl/sql.

Les trois à la fois ne sont pas possibles.

+0

Ok, ça va compliquer les choses. J'ai un type d'objet avec plusieurs constructeurs, ils ne prennent pas tous les mêmes arguments, mais ils ont tous 4-5 lignes de code commun. J'avais déjà trouvé que PL/SQL n'autorisait pas le chaînage des constructeurs, alors j'ai pensé pouvoir mettre le code d'installation commun dans une méthode privée. Qu'est-ce qu'une bonne solution PL/SQL pour ce genre de problème? – PrivateMethodMan

+0

Je ne sais pas. Peut-être que vous pouvez créer un paquet avec des fonctions surchargées qui renvoient un objet? Je n'ai pas Oracle disponible maintenant, donc je ne peux pas essayer. Considérez cela comme une usine alternative. – tuinstoel

+0

En fait ... Cela soulève un autre bon point: Si je trouve du code commun dans plusieurs méthodes dans un type d'objet, comment le refactoriser? Je ne peux pas le factoriser dans une méthode privée, est-il possible de le factoriser dans une méthode publique qui ne peut pas être appelée par quelque chose en dehors de la classe dans laquelle elle existe? Ou devons-nous simplement le documenter comme une «méthode interne de tenue de maison? et espère que personne ne l'appelle en dehors de la classe d'origine? – PrivateMethodMan

2

Ok, voici une solution potentielle que je l'ai testé très brièvement, et il semble fonctionner jusqu'à présent:

Créer un type d'objet parent qui marqué comme non définitives et sans INSTANTIABLE puis mettre tout le code privé dans Là. Les méthodes privées ne seront pas vraiment privées, mais les mettre dans un type qui n'est pas définitif et non instanciable les empêche d'être appelé. Dans le sous-type instanciable, référence les méthodes "privées" dans le supertype à travers SELF. Exemple:


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; 

Maintenant, je dois admettre, je ne aime pas vraiment ce modèle. Il semble maladroit et kludgy. Je vais probablement éviter les types d'objets autant que possible et m'en tenir aux paquets (ou aux types d'objets très simlpe). Un package-as-fatory ne m'aide à résoudre le problème de code commun privé pour les constructeurs, pas pour d'autres types de refactoring de code commun.

... à moins qu'il n'y ait une meilleure façon de travailler avec les types d'objets .... quelqu'un? n'importe qui?

0

La réponse à cette question est quelque peu difficile, mais je vais l'expliquer du mieux que je peux. Premièrement, ce n'est pas complètement la faute d'Oracle; ANSI SQL définit une chose appelée Abstract Data Types (ADT) qui peut être utilisée pour étendre SQL. Oracle suit assez bien leurs spécifications je pense. Une partie du manque d'encapsulation vient avec la difficulté de référencer et de stocker des objets en SQL. Je n'entrerai pas dans les détails ici cependant puisque je ne comprends pas entièrement moi-même.

Les ADT sont utiles pour structurer vos données, soit en code, soit en tables, mais elles ne peuvent pas être très complexes. Par exemple, vous ne pouvez pas avoir l'objet A qui a l'objet B qui a de nouveau l'objet A. Cela ne peut pas être stocké dans une table SQL. Vous pouvez contourner cela en utilisant REFs dans Oracle (pas sûr comment les autres fournisseurs vont à ce sujet), mais cela devient un autre problème à résoudre dans le code.

J'ai trouvé que les ADT sont bons pour les structures très simples et les méthodes de membres très simples. Quelque chose de plus élaboré nécessite des paquets. Souvent, je vais écrire un paquet qui implémente des méthodes membres pour un type d'objet donné, car vous ne pouvez pas appeler des méthodes privées à l'intérieur d'un objet.

Il est une douleur ...

0

Si vous avez juste besoin d'utiliser le sous-programme (fonction/procédure) d'un sous-programme PL/SQL ne vous permet d'imbriquer un autre dans l'intérieur subprogram bloc de déclaration.

Ce n'est pas aussi idéal que d'avoir des méthodes ou des fonctions privées, mais cela peut valoir la peine d'essayer avant de créer une hiérarchie d'héritage.

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; 

Si vous êtes sur Oracle 12, vous avez de la chance. Vous pouvez créer un package que seul votre type peut coder en utilisant la clause ACCESSIBLY BY. Dans l'exemple ci-dessous, le compilateur PL/SQL autorisera seulement le code de FOO_T à la référence 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; 
Questions connexes