Page 1 sur 1

[V4] POO & Interfaces

Publié : dim. 12/févr./2006 2:21
par LeCyb
J'avais fait y'a un moment un petit code pour montrer le fonctionnement des interfaces et grâce aux nouvelles possibilités de la V4 de PB le fonctionnement est légèrement plus simple.

D'une part y'a moyen de ne plus utiliser de Global mais en plus on peut faire joujou avec les With :).
La structure des fonctions a pu disparaître en utilisant la DataSection et l'affectation des adresses des fonctions peut se faire en une seule ligne.

Code : Tout sélectionner


; Déclaration des fonctions que verra l'utilisateur.
; Il est obligatoire de créer des fonctions pour modifier les "paramètres"
; de l'objet car les paramètres ne sont pas accessibles directement.
Interface Math_Object
  SetX(x.l)
  GetX.l()
  SetY(y.l)
  GetY.l()
  Add.l()
  Free()
EndInterface

; Déclaration de la structure "interne" de l'objet (non visible par l'utilisateur,
; d'où les fonctions).
; Ici on déclarera les variables de l'objet (x et y) mais aussi
; un pointeur pour les fonctions.
Structure Math_Object_Structure
  *Functions.Math_Object
  x.l
  y.l
EndStructure

; Les procédures de l'objet

; Le paramètre *Self permet de faire référence à l'objet qui a été créé.
; *Self permet d'accéder aux procédures et aux propriétés
; (les variables x et y) de l'objet.
Procedure SetX(*Self.Math_Object_Structure, x.l)
  *Self\x = x
EndProcedure

Procedure.l GetX(*Self.Math_Object_Structure)
  ProcedureReturn *Self\x
EndProcedure

Procedure SetY(*Self.Math_Object_Structure, y.l)
  *Self\y = y
EndProcedure

Procedure.l GetY(*Self.Math_Object_Structure)
  ProcedureReturn *Self\y
EndProcedure

Procedure.l Add(*Self.Math_Object_Structure)
  ProcedureReturn *Self\x + *Self\y
  Debug *Self\x + *Self\y
EndProcedure

; Ici c'est le "Destructeur" qui va permettre de libérer la mémoire et donc
; il détruit l'objet.
; Il peut être utile de faire certaines tâches avant le FreeMemory comme
; fermer un fichier.
Procedure Free(*Self.Math_Object_Structure)
  FreeMemory(*Self)
EndProcedure

; A l'inverse du destructeur voici le "Constructeur" qui va allouer un espace
; en mémoire pour contenir l'objet.
; Il peut être utile d'effectuer certaines initialisations de votre classe.
Procedure NewMathObject()
  ; allocation de la mémoire
  *new.Math_Object_Structure = AllocateMemory(SizeOf(Math_Object_Structure))
  *new\Functions = ?Functions ;<-- copie des adresses de nos fonctions
  *new\x = 0 ;<-- une initialisation
  ProcedureReturn *new
EndProcedure

MonMath1.Math_Object = NewMathObject() ; création d'un objet
MonMath1\SetX(5)
MonMath1\SetY(10)

MonMath2.Math_Object = NewMathObject() ; soyons fous, on va en créer un second

With MonMath2
  \SetX(MonMath1\GetX() + 1) ; on peut utiliser un autre objet dans le with
  \SetY(3)
  Debug \Add() ; petit bug dans la Beta 2, remplacez \Add() par MonMath2\Add()
  Debug MonMath1\GetX()
  Debug MonMath1\GetY()
  Debug \GetX() ; même bug
  Debug \GetY() ; et encore
  \Free() ; on libère l'objet
EndWith

MonMath1\Free() ; on détruit celui-là aussi

; Ici la section de données permet de placer les adresses des fonctions.
; Il est IMPERATIF que l'ordre des fonctions ici soit identique à l'ordre
; dans l'interface (Math_Object).
DataSection
  Functions:
    Data.l @SetX(), @GetX(), @SetY()
    Data.l @GetY(), @Add(), @Free() ;<-- on peut les placer sur plusieurs lignes
EndDataSection

Publié : dim. 12/févr./2006 2:33
par nico
Pourquoi tu n'utilise pas les listes chainées, ça permet une gestion plus facile, non?

Publié : dim. 12/févr./2006 14:52
par LeCyb
Avec une liste chaînée faut s'amuser à supprimer les éléments de la liste quand ils n'existent plus alors qu'ici il suffit de faire un Free() et le tour est joué.

L'idée c'est aussi de pouvoir faire une librairie et je trouve ça nettement plus propre sans global :D.

Maintenant rien n'empêche dans le prorgamme principal de faire un liste :).

Publié : dim. 12/févr./2006 15:24
par nico
Oui mais une seule fonction suffit à supprimer tous les éléments de la liste DeleteElement(), ça revient au même que freememory().

L'avantage des librairies, c'est justement que les globales deviennent un avantage, puiqu'elles ne sont accessibles que pour la librairie, il reste possible cependant les rendre accessible depuis l'extérieur!

Je dis ça car j'ai remarqué une tendance à l'utilisation des allocatememory au lieu des newlist, et je me demandais le pourquoi de cette tendance!

Publié : dim. 12/févr./2006 17:33
par LeCyb
Je pense que la tendance vient du fait que les listes étaient globales dans les anciennes versions et ça provoquait souvent des surprises.

Moi je trouve que l'utilisation d'une liste complique fort la chose.
Si je fais une classe HTTP utilisée dans un programme threadé qui va télécharger des fichiers, rien ne me dit que le premier thread (donc le premier objet de la classe HTTP) sera fini avant le deuxième.
Il faudra donc gérer l'effacement du deuxième sans effacer les autres (le premier et le troisième par ex) et bien entendu faudra utiliser un mutex.

La simplicité d'un FreeMemory pour moi surpasse de loin l'utilisation d'une liste + mutex, mais cela n'engage que moi bien entendu.

Publié : dim. 12/févr./2006 19:42
par nico
J'ai rien compris! :roll:

Avec une newlist, tu effaces l'élément de ton choix, je vois pas en quoi différentes instances d'une classe peuvent s'interférer et surtout le rapport avec les listes; si tu peux m'aiguiller?

Publié : dim. 12/févr./2006 20:13
par LeCyb
Dans l'exemple de fsw que tu as cité (ici) il utilise une liste globale.

Quand tu veux créer un nouvel objet de la classe (avec CreateThisClass()) un nouvel élément va être placé dans la liste.
Admettons que tu le fais 10 fois, il y aura donc on aura 10 éléments dans cette liste globale.
Si je n'utilise plus l'objet numéro 4 je dois le libérer (faisons propre) et donc je dois l'enlever de la liste.
Pour faire ça on va utiliser DeleteElement(Liste()) sauf que ça efface l'élement courant, et dans notre cas l'élément courant c'est le dixième.
Pour effacer le quatrième il faut d'abord trouver sa position dans la liste, puis se positionner dessus et enfin l'effacer.
Si la librairie est utilisée dans un environnement avec des Threads il faudra créer un mutex pour l'ajout et la supression d'élements dans la liste.

Donc je ne crois pas que...
- déclarer une liste
- gérer un mutex
- ajoutter un élément dans la liste
- trouver la position de l'élément dans la liste
- supprimer l'élément de la liste

...soit plus simple que de faire un seul et unique FreeMemory mais je peux me tromper.

Publié : dim. 12/févr./2006 20:38
par nico
Effectivement, je n'avais pas pensé à ça! :?

Publié : dim. 12/févr./2006 20:40
par Dräc
LeCyb se débrouille bien tout seul, mais je tiens à le soutenir au sujet des listes chainées.

Les listes chainées n'apportent pas un véritable interet dans la gestion des objets sinon d'offrir une énumération facile de tous les objets d'une meme classe.

Cependant, dans l'absolu, des objets d'une meme classe n'ont pas de raison d'etre liés entre eux.
Certes, ils partagent la meme défintion pour exister mais cela s'arrete là!

Bref, les listes chainées donnent l'impression de maitriser la gestion des objets, mais les objets étant responsables de leur propre état, cela n'est qu'un impression.

Sinon chacun fait comme il lui plait ;)

Publié : dim. 12/févr./2006 21:28
par LeCyb
Merci Dräc mais c'est certainement grâce à toi (enfin ton site).

Les listes chaînées sont utiles mais devraient pour moi être gérées dans le programme qui utilise la classe (qui pour faire propre devrait être dans un fichier séparé).

Un exemple qui me semble bon c'est une classe Monstre et une classe Joueur.
Un jeu de combat aurait 1 objet Monstre et 1 objet Joueur.
Un jeu de rôle aurait lui une liste d'objets Joueurs et une liste d'objets Monstres.