Quoi, tu ne connais pas PB ? Va falloir parcourir tout le forum alors !

Le forum (ô combien francophone) des utilisateurs de Powerbuilder.

Recherche rapide

Annonce

Certaines rubriques, dont des cours, sont uniquement visibles par les membres du forum ^^.
Dans la rubrique Liens & Références, vous avez accès à un sommaire de téléchargement, profitez-en !
Il existe maintenant un nouveau TOPIC "Votre CV en Ligne" accessible uniquement par demande.

#1 27-03-2008 10:42:33

Cortex  
Modérateur
Lieu: Arlon
Date d'inscription: 08-02-2008
Messages: 194
Pépites: 6,904
Banque: 2,109,818,425,070

[ASTUCE] Interfaces en PB

J'ai trop souvent entendu des developpeurs devant apprendre PB se plaindre de l'inexistance d'interfaces en PB, "c'est vraiment nul, comment veux-tu faire de la prog propre sans ca?"... Je vais donc vous démontrer ici une bonne fois pour toute qu'en PB, tout est possible!

1. But

Je desire obtenir un moyen de rassembler des classes heteroclytes, pour les faire partager certains traitements p.ex., mais sans pour autant alourdir mes herarchies d'heritage. En Java ou .NET j'utiliserais donc des Interfaces et en C++ ou Eiffel l'héritage multiple... Ce qui par ailleurs, a un certain niveau conceptuel, est relativement similaire...

Meme si le principe n'existe pas de lui meme en powerscript, je pars du principe que l'interfacage est avant tout un design, qu'une fonctionnalite specifique a un langage. Je garde donc ce terme d'"Interface" par commodite...

2. Besoins

Il me faut:
1 - une classe non instantiable, decrivant une serie de methodes publiques sans les implementer, pour servir de "proxy" commun
a une serie d'implementations.
2 - un moyen de lier en developpement une telle interface avec n'importe quelle autre classe devant l'implementer
3 - un moyen de palier l'absence de casting explicite en PB, pour transtyper une implementation en une de ses interfaces...
4 - quelques conventions pour la forme (mais pas trop)
5 - une ch'tite dose d'imagination...

3. Methodes

Pour le besoin#1, le plus simple est qu'une interface soit une simple classe non instantiable, ce seront ses methodes publiques qui décriront sont contrat (on peut rajouter les event aussi si on veut). Ce sera donc notre 1ere convention, la 2nde etant de dire que le nom de ces classes devrait etre préfixé de "i_" pour les differentier totalement des nvo standards.
Ces "Interfaces" auraient bien besoin aussi d'un ancetre commun, deja pour contenir certaines verifications decrites plus bas. Appellons-le "n_interface", qui sera abstraite (voir mon post http://pbadonf.fr/forum/viewtopic.php?id=1507)
Par contre pour le besoin#2, la c'est moins evident... Pas mal de possibilites sont possibles, mais beaucoup rendent la maintenance ou l'utilisation des interfaces fastidieuse en developpement. Personellement, j'apprecie particulierement la solution suivante:
Pour qu'une classe implemente une interface, on lui ajoute cette interface en nested nvo (menu Insert>Object>User Object...). Il est donc tres facile de voir quelles interfaces une classe implemente, pas besoin de se soucier des create/destroy et tres facile d'en rajouter (autre convention que j'utilise: nommer ces nested class comme l'interface correspondante, mais en omettant le "_").
L'interface peut donc toujours avoir acces a son implementation courante via le pronom "parent", et si d'ailleurs ce pronom est "not valid()", on sait de suite que qqn tente d'instancier directement l'interface en niant totalement nos conventions -> runtimeerror!
Ce qui resout egalement le besoin#3: pas besoin de casting, puisque chaque classe devant supporter une interface aura fatalement une nested class d'un nom standardise renvoyant bien une instance de l'Interface specifique a notre classe.

4. Codes

Dans l'ancetre "n_interface", on va rajouter une methode, qui aura pour but de verifier dans le constructeur que "parent" implemente bien les methodes definie dans son interface, en verifiant bien les types de retour et d'arguments.

Code: pb

private function boolean uf_isimplementedby (powerobject ao_object);
// Renvoie true si ao_object implemente bien toute les methode publique
// de l'Interface
if not isvalid(ao_object) then return false

classdefinition lcd_this, lcd_impl
scriptdefinition lsd_this[], lsd_impl[]

// on recupere les classdefinition de l'Interface et de la classe
lcd_this = this.classdefinition
lcd_impl = ao_object.classdefinition

// puis l'ensemble de leurs methodes
lsd_this[] = lcd_this.scriptlist[]
lsd_impl[] = lcd_impl.scriptlist[]

long ll_i, ll_imax, ll_j, ll_jmax
boolean lb_ret = true, lb_found_matching_script = false

ll_jmax = upperbound(lsd_impl[])
ll_imax = upperbound(lsd_this[])
ll_i = 1
do while ll_i <= ll_imax and lb_ret

   // on ne test que les METHODES PUBLIQUES de l'Interface
   if lsd_this[ll_i].access = public! and lsd_this[ll_i].kind = scriptfunction! then
      ll_j = 1
      lb_found_matching_script = false
      do while ll_j <= ll_jmax and not lb_found_matching_script
         if lsd_impl[ll_j].name = lsd_this[ll_i].name then
            // est-ce un equivalent?...
            lb_found_matching_script =this.uf_matching_function( lsd_this[ll_i], lsd_impl[ll_j])
         end if
         ll_j ++
      loop
      lb_ret = lb_found_matching_script
   end if
   ll_i ++
loop

return lb_ret
end function

private function boolean uf_matching_function (ref scriptdefinition asd_current, ref scriptdefinition asd_object);
// Le script de la classe (asd_object) a-t-il les meme arguments que celui de l'Interface (asd_current)?
// Et le type de retour?
if not isvalid(asd_current) or not isvalid(asd_object) then return false
if isvalid(asd_current.returntype) <> isvalid(asd_object.returntype) then
   return false
else
   if isvalid(asd_current.returntype) then
      if asd_current.returntype.name <> asd_object.returntype.name then return false
   end if
end if
variabledefinition lvd_current[], lvd_object[]
long ll_i, ll_imax, ll_j, ll_jmax
boolean lb_ret = true, lb_found = false

lvd_current[] = asd_current.argumentlist[]
lvd_object[] = asd_object.argumentlist[]

ll_jmax = upperbound(lvd_object[])
ll_imax = upperbound(lvd_current[])

if ll_imax <> ll_jmax then return false
   ll_i = 1
   do while ll_i <= ll_imax and lb_ret
      ll_j = 1
      lb_found = false
      do while ll_j <= ll_jmax and not lb_found
         if lvd_current[ll_i].callingconvention = lvd_object[ll_j].callingconvention and lvd_current[ll_i].typeinfo.name = lvd_object[ll_j].typeinfo.name then
            lb_found = true
         end if
         ll_j ++
      loop
      lb_ret = lb_found
      ll_i ++
   loop
return lb_ret
end function


Et dans le constructeur de n_interface:

Code: pb

event constructor;
runtimeerror lrte_error
   
// abstract class
populateerror(0, '')
if this.classname( ) = error.object then
   lrte_error = create runtimeerror
   lrte_error.setmessage( 'Abstract class ~'' + this.classname( ) + '~' could not be instantiated.' )
   throw lrte_error
end if


// Is it a nested class?
if isvalid(this.getparent()) then

   // then check if methods are implemented...
   if not this.uf_isimplementedby( parent ) then
      classdefinition lcd_this
      lcd_this = this.classdefinition
      lrte_error = create runtimeerror
      lrte_error.setmessage( 'Class "' + classname(parent) + '" does not implement interface "' + lcd_this.ancestor.name + '".' ) //note: I called the ancestor's classname, 'cause this correspond to the nested class and is something like 'n_class`iinterface' which is not proper...
      throw lrte_error
   end if
else
   // ...it is not a nested class...
   lrte_error = create runtimeerror
   lrte_error.setmessage( 'Interface "' + this.classname() + '" must be implemented by a concrete class.' )
   throw lrte_error
end if

end event



Il ne reste des lors qu'a creer des interfaces! Par exemple, I_cloneable:

Code: pb

forward prototypes
public function powerobject clone ()
end prototypes

public function powerobject clone ();return parent.dynamic clone()
end function


Remarquez le "return parent.dynamic clone()". C'est une contrainte de mon architecture (que j'expliquerai peut etre un jour comment pallier si j'ai le temps). C'est la cle de voute du truc: quand on appellera une methode de l'interface d'une classe, celle-ci dispatchera a son implementation...
Toute methode d'une interface devra donc contenir un appel en dynamic de la meme methode sur son "parent".

Et donc, imaginons une classe n_personne implementant I_cloneable grace a une nested class de nom icloneable. On peut donc utiliser le code:

Code: pb

n_personne p1, p2

p1 = create n_personne
p1.setnom('Ouic')
p1.setprenom( 'Pick' )

p2 = p1.icloneable.clone( )
...

Ce qui non seulement appelle la methode "clone" de personne, mais surtout permet p.ex. de stocker p.icloneable dans un tableau de type i_cloneable des interfaces de toutes sortes d'objets implementant i_cloneable sans qu'il heritent les uns des autres...

Et ca marche aussi sur un visual UO...

Perso, je l'utilise assez souvent, c'est impressionant les perspectives que ca ouvre...

Alors?
C'est pas super cool?

Hors ligne

 

#2 27-03-2008 11:04:24

foon  
N2iGeek + MangasGeek = foon
Award: bf
Lieu: Bonchamp-Lès-Laval
Date d'inscription: 28-02-2007
Messages: 2487
Pépites: 88
Banque: 9,223,372,036,854,776,000

Re: [ASTUCE] Interfaces en PB

000 pépites pour cette astuce de souris-géniale-qui-tente-tous-les-jours-de-conquérir-le-monde


Seuls ceux qui ne font rien ne font jamais d'erreurs
http://www.nerdtests.com/images/badge/163124fb7fb459a3.gif

Hors ligne

 

#3 27-03-2008 11:05:19

erasorz  
Admin
Lieu: Babylone
Date d'inscription: 23-11-2006
Messages: 5121
Pépites: 97,197
Banque: 2,147,483,647

Re: [ASTUCE] Interfaces en PB

pour cette contribution

0.000 pépites


N'envoyez jamais un humain faire le travail d'un programme.

Hors ligne

 

#4 27-03-2008 12:12:45

Cortex  
Modérateur
Lieu: Arlon
Date d'inscription: 08-02-2008
Messages: 194
Pépites: 6,904
Banque: 2,109,818,425,070

Re: [ASTUCE] Interfaces en PB

Bon ca...

Me reste plus qu'a trouver un site ou je peux louer des mercenaires avec des PBAdonf-pepites...
Avec un peu de chance d'ici lundi je deviens empereur de belgique... et apres le MONDE!!!

en tout cas...

Hors ligne

 

Pied de page des forums

Propulsé par FluxBB 1.2.22