Aide - Recherche - Membres - Calendrier
Version complète : [contribution] Collection D'objets
La Communauté TitaXium > Service Communication > Espace Developpement
Méthylbro
bon voilà ; l'un des concepts de la POO c'est les collections d'objets.

Je me suis pas mal torturé l'esprit ces dernières semaines pour créer des collections d'objets avec php5. Mon premier reflexe était de créer ma liste d'objet dans un tableau ...
Code
$objet1 = new MyClass();
$objet2 = new MyClass();
$objet3 = new MyClass();

$collection = array($objet1, $objet2, $objet3);

Mais cette première solution ... ca ne respecte pas du tout les concepts de la POO.

C'est donc là que j'ai commencé à créer pour chacune de mes classes ; une classe MyClassList. Mais c'est vachement lourd. Alors je me suit dit ; pourquoi ne pas créer une classe contenant une liste d'objets ... Quelque chose que l'on pourrait utiliser comme ca :

Citation
$objets = new Collection('MyClass') // objets est une Collection de MyClass
$objets->insert( new MyClass() );
$objets->insert( new MyClass() );
$objets->insert( new MyClass() );

Avec tout un ensemble de méthodes pour gérer ma collection.

J'ai donc codé la classe ci-jointe au message. J'aimerais des avis ; des critiques ; des suggestions etc ...
Par exemple sur la gestion d'erreurs qui n'est pas superbe pour le moment.

Besoin d'un avis extérieur donc.
Fantome
Bonsoir,

Bon je me lance, ça risque d'être un peu fouillis.

Pour commencer je crois qu'il y a une petit erreur dans le code (ligne 151), tu appelé la fonction $this->existe($object) or elle est déclarée comme suis public function if_exists( $object ).

Une question un peu plus intéressante : y a t-il une raison pour que les valeur par défaut de clés soit différentes entre get_object( $key = 0 ) et delete( $key = NULL ) ?

Lors de l'utilisation de la fonction get_key( $object ) il est impossible faire la différence entre l'objet trouvé à l'index 0 et l'objet non trouvé. Je pense qu'il serait plus judicieux de remplacer le 0 (dans $result = 0;) par une valeur qui ne peut correspondre à une clé du tableau, exemple : -1 ou NULL.

Dans la fonction get_range($low = 0, $high = 0, $step = 1) il serait préférable de faire les test sur les paramètres fournis : valeur entière, >=0, <count().

Pour ce qui est de la gestion des erreur l'ajout de code d'erreur dans le retour pourrait permettre de facilité sont utilisation, surtout lors des phases de debuguage.
En plus de ces codes d'erreurs une méthode renvoyant un message (descripteur de l'erreur) surtout en PHP ou le typage est implicite. Je pense notamment, que lors de test de type pour l'ajout d'objets, il peut être intéressant de donnée le type attendu et le type reçu.

Au niveau de l'ajout de fonctionnalité, je pensé à un équivalent de array_map(), qui retournerai un tableau des résultats de l'appel d'une méthode de MyClass sur chacun des objets.
Comme évoqué dans le sujet [POO] Liste d'objets (sur developpez.com), des itérateurs peuvent être ajouté (ex: first, prev, next, last), même si l'intérêt reste limité étant donné que nos clés sont des entiers consécutifs.

bonne nuit.
Méthylbro
Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Pour commencer je crois qu'il y a une petit erreur dans le code (ligne 151), tu appelé la fonction $this->existe($object) or elle est déclarée comme suis public function if_exists( $object ).

En Effet ; merci.
J'avais écrit la classe avec des noms de méthodes en français au début. Mais je me suis dit qu'en anglais c'était mieux. D'ou cet oubli.

Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Une question un peu plus intéressante : y a t-il une raison pour que les valeur par défaut de clés soit différentes entre get_object( $key = 0 ) et delete( $key = NULL ) ?

Bah ; je m'étais dit que :
- lors de la selection d'un élément de la collection à un indice donnée ; prendre le premier ou le dernier indice par défaut était logique. Et comme prendre le premier était plus facile à utiliser ...

Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Lors de l'utilisation de la fonction get_key( $object ) il est impossible faire la différence entre l'objet trouvé à l'index 0 et l'objet non trouvé. Je pense qu'il serait plus judicieux de remplacer le 0 (dans $result = 0;) par une valeur qui ne peut correspondre à une clé du tableau, exemple : -1 ou NULL.

Voilà un cas auquel je n'avais pas pensé. Merci de ta remarque ; je vais trouvé un moyen de retourner ce type d'infos.

Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Dans la fonction get_range($low = 0, $high = 0, $step = 1) il serait préférable de faire les test sur les paramètres fournis : valeur entière, >=0, <count().

Idem, excellente remarque aussi. T'est génial ... un vrai bonne critique tongue.gif

Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Pour ce qui est de la gestion des erreur l'ajout de code d'erreur dans le retour pourrait permettre de facilité sont utilisation, surtout lors des phases de debuguage.
En plus de ces codes d'erreurs une méthode renvoyant un message (descripteur de l'erreur) surtout en PHP ou le typage est implicite. Je pense notamment, que lors de test de type pour l'ajout d'objets, il peut être intéressant de donnée le type attendu et le type reçu.

J'avais pensé à utiliser les exceptions pour gérer les erreurs les moins critique ; et trigger_error() pour les plus critiques. Mais les exceptions me faut tout aussi peut qu'elles ne m'attirent. J'ai toujours peur de ralentir mes scripts en les utilisants (depuis que j'ai lu un bout d'article disant qu'elles étaient a utiliser avec des pincettes ...). T'en pense quoi toi ?

Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Au niveau de l'ajout de fonctionnalité, je pensé à un équivalent de array_map(), qui retournerai un tableau des résultats de l'appel d'une méthode de MyClass sur chacun des objets.

C'est a dire ; une méthode permettant d'effectuer des opérations en cascades sur les objets de ma collection ?

Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *
Comme évoqué dans le sujet [POO] Liste d'objets (sur developpez.com), des itérateurs peuvent être ajouté (ex: first, prev, next, last), même si l'intérêt reste limité étant donné que nos clés sont des entiers consécutifs.

Urg ?
C'est toi le gentil développeur de developpez.net qui a répondu a mes questions ?
Incroyable ... Mais je ne suis tranquille nulle part. Les titaxiens sont partout !
Fantome
Citation (méthylbro @ samedi 24 novembre 2007 à 01h01) *
Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *

Pour ce qui est de la gestion des erreur l'ajout de code d'erreur dans le retour pourrait permettre de facilité sont utilisation, surtout lors des phases de debuguage.
En plus de ces codes d'erreurs une méthode renvoyant un message (descripteur de l'erreur) surtout en PHP ou le typage est implicite. Je pense notamment, que lors de test de type pour l'ajout d'objets, il peut être intéressant de donnée le type attendu et le type reçu.

J'avais pensé à utiliser les exceptions pour gérer les erreurs les moins critique ; et trigger_error() pour les plus critiques. Mais les exceptions me faut tout aussi peut qu'elles ne m'attirent. J'ai toujours peur de ralentir mes scripts en les utilisants (depuis que j'ai lu un bout d'article disant qu'elles étaient a utiliser avec des pincettes ...). T'en pense quoi toi ?

Les expressions sont bien pratique lorsqu'on utilise dans de long bloc de code plusieur fois des fonctions qui ont des chances d'échoué, et le cas échéant doivent stopper l'exécution du code, ça évite de faire des tests toutes les 3 lignes.
Après niveau performance je n'est aucune idée de leur impact. C'est une question intéressante.

Citation (méthylbro @ samedi 24 novembre 2007 à 01h01) *
Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *

Au niveau de l'ajout de fonctionnalité, je pensé à un équivalent de array_map(), qui retournerai un tableau des résultats de l'appel d'une méthode de MyClass sur chacun des objets.

C'est a dire ; une méthode permettant d'effectuer des opérations en cascades sur les objets de ma collection ?

Demain j'essayerais de faire une explication un peu plus claire, mais là j'ai un peu du mal à formuler mes idées.

Citation (méthylbro @ samedi 24 novembre 2007 à 01h01) *
Citation (Fantome @ samedi 24 novembre 2007 à 00h35) *

Comme évoqué dans le sujet [POO] Liste d'objets (sur developpez.com), des itérateurs peuvent être ajouté (ex: first, prev, next, last), même si l'intérêt reste limité étant donné que nos clés sont des entiers consécutifs.

Urg ?
C'est toi le gentil développeur de developpez.net qui a répondu a mes questions ?
Incroyable ... Mais je ne suis tranquille nulle part. Les titaxiens sont partout !

Non, c'est pas moi, c'est juste qu'en tombant sur ton post ici, j'ai cherché a me documenter un peu sur les collections, et le post sur developpez.net était les deuxième de Google ^^
Méthylbro
Pour ce qui concerne les Exceptions ; j'ai quand même envie d'essayer. Je pense qu'a ce niveau d'abstraction ; cette méthode de gestion d'erreur est la plus logique.

Et puis ; ca me permettra de tester réellement ce que cela consomme en termes de ressources.
Fantome
pour la fonction "map" je penser à quelque chose du genre :
Code
/**
* @param $methode string nom de la methode à appeler
* @param $param array() liste des paramétre à passer à la fonction
* @return array() liste des résultats
*/
public function map($methode = '', $param = NULL)
{
    $result = array();
    
    foreach ($this->list as $key => $object)
    {
        eval ( '$result['.$key.'] = $this->list['.$key.']->'.$methode.'('.implode(', ', $param).');' );
    }
    
    return $result;
}


voila en gros a quoi je pense, bien sur il faut rajouter quelque test sur les variables et peut être quelques autres choses, mais l'idée est là.
Méthylbro
l'idée n'est pas mauvaise ; je l'ai reprise en essayant de la séccurisée un peut plus.
Que pense tu de ca :

Code
/**
* Execute la méthode passé en paramètres sur toute la collection d'objets.
* @author Fantome@titaxium.org
* @param $methode string nom de la methode à appeler
* @param $param array() liste des paramétre à passer à la fonction
* @return array() liste des résultats
*/
public function map( $name = NULL, $params = NULL ) {

  if ( in_array($name, get_class_methods($this->type)) ) {

    $result = array();
    $tmp = $this->list;

    foreach ($tmp as $key => $object) {

      $method = $name.'('.implode(', ', $params).')';
      $object = $this->list['.$key.'];

      try {

        eval ( '$result['.$key.'] = '.$object.'->'.$method.';' );

      } catch (Exception $e) {

        throw $e;

      }

    }

  $this->list = $tmp;

  } else {

    $msg = "Method <em>".$type."::".$name."</em> was not declared";
    trigger_error($msg, E_USER_ERROR);

  }

  return $result;

}


Maintenant je me pose une question ;

il serait possible de permettre à des classes héritées de MyClass d'être contenues dans la collection. Et-ce que je le fait ? Ou n'y a t-il aucun interet ?

J'essaye ? ou pas ?
Fantome
ça me semble pas mal, mais j'ai pas compris l'utilité de $tmp.


à la fin dans $msg =... il faut remplacer $type par $this->type.



Pour la question des classes héritées je sais pas. ça dépend de quel façon on souhaite voir les différentes classes qui hérite de MyClass. Si on fait une collection de MyClass + hérité, ça reviens a traiter toute les class hérité de la même manière, sans distinction possible. Une autre possibilité peut être de faire une collection de collection de classe hérité de MyClass. Mais je ne sais pas n'y si l'une de ses solutions peut avoir un intérêt, n'y laquelle est préférable.
Méthylbro
le tmp ; c'est pour éviter qu'une partie avant la reception d'une exception ai été changé par la méthode ; et que l'autre non.

En gros les objets de la collections sont modifiés par la méthode si et seulement si il n'y a pas d'erreurs.

ca sauvegarde l'intégrité des données en gros. Un peu comme un COMIT en sql.
Fantome
Citation (Moi@edit)
Pour la question des classes héritées je sais pas. ça dépend de quel façon on souhaite voir les différentes classes qui hérite de MyClass. Si on fait une collection de MyClass + hérité, ça reviens a traiter toute les class hérité de la même manière, sans distinction possible. Une autre possibilité peut être de faire une collection de collection de classe hérité de MyClass. Mais je ne sais pas n'y si l'une de ses solutions peut avoir un intérêt, n'y laquelle est préférable.




Effectivement j'avais pas penser à ça. Mais la ligne $object = $this->list['.$key.']; vas justement nous ramener sur la vrai liste au lieu de travailler sur la copie.
Méthylbro
ah oui merde xD

je suis trop con

EDIT : J'ajoute en piéce jointe là ou nous en sommes ; si jamais ca intéresse quelqu'un (ce quelqu'un peut venir nous aider quand il veux d'ailleurs ...)
Cliquez pour voir le fichier-joint
Fantome
t'as oublier de changer le $tmp dans foreach ($tmp as $key => $object)

d'ailleur si tu fais foreach ($this->list as $key => $object), la ligne $object = $this->list['.$key.']; deviens inutile (surtout qu'il y a une erreur il n'y a pas besion de '. et .')

Il faudrait vérifier qu'il ne travail pas sur une copie de l'objet, le plus sûr à mon avis est de remplacer $object par $this->list[ $key ] dans l'eval.

PS: il doit bien avoir d'autre personnes qui regarde vue le nombre de téléchargement.
Méthylbro
Bon ; nouvelles fonctionalités ce matin.

Je me suit dit qu'il serait très utile de pouvoir parcourir une collection avec un foreach. J'ai donc regardé la documentation sur les itérateurs ; et voici ce que j'ai rajouté à la classe :

Citation
/**
* Rembobine le pointeur au premier élément de la collection.
* @access public
*/
public function rewind() {

reset($this->list);

}

/**
* Retourne l'élément courant de la collection.
* /!\ L'élément est assigné par référence !
* @access public
* @result Object
*/
public function current() {

$result = &curren;t($this->list);
return $result;

}

/**
* Retourne la clé l'élément courant de la collection.
* @access public
* @result int
*/
public function key() {

$result = key($this->list);
return $result;

}

/**
* Avance le pointeur d'un élément dans la collection.
* @access public
* @result Object, boolean
*/
public function next() {

$result = next($this->list);
return $result;

}

/**
*
*/
public function valid() {

$result = $this->current() !== false;
return $result;

}


Ci-join ; la dernier release.
ED
Flemme de tout lire.

Mais deux remarques:

Jette un coup d'oeil à l'interface Collection en java çà pourrait te donner des idées: http://java.sun.com/j2se/1.5.0/docs/api/ja...Collection.html

Sinon PHP5 supporte le typage pour tes objets, utilise le ca sera plus propre que de le passer à l'instantiation.


EDIT:
Implémenter iterator est un non sens.

Une collection est un conteneur. Un iterator une méthode de parcours.

Par contre ta collection peut retourner un iterator et toi créer une class CollectionIterator qui implémente effectivement iterator.
Méthylbro
Je me suis posais toutes ces questions quand j'ai commencé à travailler dessus.

Il est au contraire logique de passer le type à l'instanciation. C'est ce qu'on fait en java ; et c'est même ce qu'on fait en algo.

Exemple de l'instanciation d'une collection en algo :
Code
MyCollection est une Collection de MyClass


Pour ce qui est de l'itérateur ; suffit de regarder la doc de la classe Collection de JAVA :
Citation
public interface Collection<E>extends Iterable<E>

C'est logique que si les méthodes de l'interface Itérateur soient là pour décrire le parcours d'un objet ; Collection implémentera cette idte interface :s

Sinon ; merci de tes remarques ED smile.gif
c'est toujours bon la critique ^^
ED
Attention Iterable signifie que tu peux utiliser un iterateur dessus hein tongue.gif

Ca veut pas dire que c'est un iterateur.
Méthylbro
ok ; donc tu me conseillerais de mettre mes méthodes next() current() etc ... dans une classe CollectionIterateur par exemple ?

Ou serais l'intêret ... moi ce que je veux ; c'est pouvoir faire un foreach directement su un objet Collection ...

je ne te comprend pas ...
Explique moi ca s'il te plait
ED
en java ca fonctionne comme ca :
Code
// On instancie un vector c'est un type particulier de collection
Vector<MonObjet> v = new Vector<MonObjet>();
// On le remplit un peu :p
v.add(new MonObjet(1));
v.add(new MonObjet(2));
v.add(new MonObjet(3));

// Maintenant on veut parcourir le Vector
Iterator i = v.iterator();
while(i.hasNext()) {
    MonObjet tmp = i.next();
    tmp.doIt();
    ....
}


Un collection représente un ensemble d'objet. C'est une interface.

Après tu peux choisir comment tu stockes tes objets en implémentant l'interface.
Exemple: Une liste, un vecteur.

Mais ca reste un conteneur, un outil de stockage c'est tout.

Un iterateur est l'outil qui te permet de parcourir ce conteneur. Comme une collection ne te dis pas comment on parcourt, tu ne peux savoir comment implémenter la recherche.
Tu passes donc par un itérateur.

Et pour spécifier que que ta classe est iterable bas tu lui fait implémenter iterable ce qui signifie que ta "collection" possède une méthode iterator() qui retourne un iterator.
Méthylbro
il me semble avoir compris. Merci ED ^^
Méthylbro
Bon ; je pense avoir fait le tour de ce probléme.

J'ai modifié en fonction des remarques de ED. J'ai réglés les problémes que posais la méthode map() (on ne pouvais pas passer d'objets en paramètres).

Je pense que j'ai fait le tour.

D'autres critiques ?
Méthylbro
CORRECTION DE BUG.

Pour le moment ; la collection ne gérée pas les classes héritées.
Il s'agit juste d'une erreur idiote de ma part. Pour résoudre se probléme ; il suffit d'utiliser instanceof dans la méthode verif_type() comme ci-dessous :
Code
/**
* Retourne vrai si le type de l'objet passé en paramètre
* correspond au type d'objets de la collection.
* @access private
* @param Object $object
* @result boolean
*/
public function verif_type( $object ) {

    if ( is_object($object) ) {

        if ( $object instanceof $this->type) {

            $result = true;

        } else {

            $result = false;

        }

    } else {

        $result = false;

    }

    return $result;

}
Méthylbro
Nouvelle version.

Surcharge de propriétées et de méthodes ajoutées. Et d'autres choses.
Au lieu de faire des tonnes de commentaires ; voyer plutot la source ci-jointe.
dig
Je n'arrive pas à accéder aux pièces-jointes : pas la permission... normal ?

Sinon, si tu souhaites développer un truc sérieux pour gérer des collections en PHP, j'veux bien participer =).
Méthylbro
Et bien ; j'avoue que ; comme la SPL ne gère pas les collections ; ca me botterais bien de proposer une librairie complète la dessus.

Maintenant ; vaudrais que ce soit hyper propre ; et pas du bricolage comme cette classe là.

contacte moi par mp; mail ou irc on en resdiscutteras ^^
dig
Bah t'as lancé un sujet là-dessus j'vois pas pourquoi on en profiterais pas ^^
Ceci est une version "bas débit" de notre forum. Pour voir la version complète avec plus d'informations, la mise en page et les images, veuillez cliquer ici.
Invision Power Board © 2001-2010 Invision Power Services, Inc.