begin process at 2012 05 30 19:46:19
  Trouver un code source :
 
dans
 
Accueil > Forum > 

PHP

 > 

POO

 > 

Classes & Objets

 > 

Interface Iterator et problème de conception.


Derniers messages déposésPoser une question dans le forum ou lancer une discussion

Interface Iterator et problème de conception.

mardi 27 novembre 2007 à 23:11:16 | Interface Iterator et problème de conception.

LocalStone

Salut à tous,
Alors voilà ... Un nouveau post, un nouveau problème ! Mais par contre, on continue avec l'interface Iterator.
Pour un projet, j'ai du coder une classe Map qui a la donnée d'une clé de n'importe quel type (string, int, object, etc.) retourne une valeur. Le fait que la clé soit de n'importe quelle type m'empêche d'utiliser un tableu associatif. Lorsque j'ai codé la classe tout allait bien jusqu'à ce que je me rende compte d'un soucis assez problématique avec la méthode Map::removeValue($mxdKey) ... Mais je ne sais pas s'il vient de ma manière de faire ou bien de PHP.
Histoire de bien comprendre le soucis, je vais vous expliquer concrêtement le problème à l'aide d'une classe d'exemple que j'appellerais Conteneur à laquelle on impose les contraintes suivantes :
  • on doit pouvoir la parcourir avec un foreach() ;
  • on doit pouvoir supprimer des élements.
Pour cela, je propose la solution suivante : la classe possède un attribut $Elements du type array() et implémente l'interface Iterator. De plus, la classe possède une méthode Conteneur::supprimerElement($strIndice) qui permet de supprimer l'entrée correspondant à la clé passée en paramètre. Voici donc le code produit ici.
Le soucis est le suivant, imaginons ce code ci ... Et bien ça ne marche pas du tout ... Le foreach est cassé lors de la suppression d'un element du tableau ... Et j'ai essayé un tas de truc (mais j'en parle pas pour l'instant, histoire d'induire personne en erreur et de laisser libre cours à votre imagination), mais rien n'y fait. De plus, dans l'exemple que je propose, on essaye de supprimer le premier élement du tableau, donc c'est un cas particulier qui pose problème, mais il y a un problème quelque soit la position de l'élement.
Donc voilà ... Si quelqu'un à une idée, une solution, etc. ... Je suis preneur, encore une fois !

LocalStone
mardi 27 novembre 2007 à 23:18:08 | Re : Interface Iterator et problème de conception.

LocalStone

Le site où j'ai posté le code les supprime au bout de 24 heures, donc je les remets là aussi, au cas où :
La classe Conteneur
<?php
/* -------------------- */
class Conteneur implements Iterator {
 
private $Elements = array();
 
public function __construct($arrElements = array()) {//$arrMap = array()) {
$this -> Elements = $arrElements;
}
 
public function supprimerValeur($strIndice) {
unset($this -> Elements[$strIndice]);
}
 
public function recupererValeur($strIndice) {
return $this -> Elements[$strIndice];
}
 
public function key() {
return key($this -> Elements);
}
 
public function current() {
return current($this -> Elements);
}
 
public function next() {
next($this -> Elements);
}
 
public function rewind() {
reset($this -> Elements);
}
 
public function valid() {
return current($this -> Elements) !== false;
}
 
}
/* -------------------- */
?>

L'exemple
<?php
/* -------------------- */
$arrTableau = array(
'Indice n°1' => 'Salut',
'Indice n°2' => 'Les',
'Indice n°3' => 'Amis',
'Indice n°4' => 'Comment',
'Indice n°5' => 'Allez',
'Indice n°6' => 'Vous'
);
/* -------------------- */
$objConteneur = new Conteneur($arrTableau);
foreach ($objConteneur as $strIndice => $strValeur) {
if ($strIndice == 'Indice n°1') {
$objConteneur -> supprimer($strIndice);
} else {
echo'$arrTableau[' . $strIndice . '] = ' . $strValeur . ';<br />';
}
}
/* -------------------- */
?>
LocalStone
mardi 27 novembre 2007 à 23:56:29 | Re : Interface Iterator et problème de conception.

neigedhiver

Salut, Tu dis : "De plus, la classe possède une méthode Conteneur::supprimerElement($strIndice)" Dans le code de ta classe : public function supprimerValeur($strIndice) { unset($this -> Elements[$strIndice]); } Dans le code exemple : $objConteneur -> supprimer($strIndice); Mets-toi d'accord avec toi-même : comment tu veux l'appeler cette méthode ? En fait, ce sur quoi tu itères, c'est un Array... Utilise simplement la classe ArrayIterator, qui permet déjà de supprimer un élément du tableau avec ArrayIterator::OffsetUnset() A moins que ta classe n'ait besoin de quelque chose de plus complexe, mais d'après ton exemple, ça ne semble pas être le cas... Ou alors ton exemple n'est pas approprié... Parce qu'en plus dans ton code, y'a des erreurs. current ne doit pas retourner le tableau sur lequel tu itères (c'est ce que tu fais) mais l'élément sur lequel le pointeur est positionné. current() doit donc faire ce que tu fais dans recupererValeur() Sauf que si à recupererValeur tu passes en argument l'indice de l'élément à récupérer, ta classe n'est plus seulement un itérateur, mais aussi un accesseur. C'est dommage, ArrayIterator est déjà un accesseur, puisqu'elle implémente ArrayAccess. Donc je pense que tu te compliques la vie pour pas grand chose ;)
mercredi 28 novembre 2007 à 00:09:14 | Re : Interface Iterator et problème de conception.

LocalStone

Exacte pour les noms de méthode ... J'ai pas trop vérifier lorsque j'ai viré les 25000 tests. On va l'appeller Conteneur::supprimerValeur() !
Ensuite, je suis bien conscient que ArrayIterator existe et c'est pourquoi j'ai précisé que ma classe d'exemple est naze. Mais dans le problème que j'ai est un peu plus compliqué, et j'aimerais éviter d'utiliser ArrayIterator, même si - au final - cette classe d'exemple risque d'être vraiment semblable a celle de la SPL. Pour être consi, c'est à cause du fait que la clé d'un iterateur ne peut pas être un objet. Dans la classe que je poste, il n'y a pas cette gestion des objets en temps que clé puisqu'ellle sert vraiment d'exemple, mais une fois ce problème résolu, j'ajouterais la possibilité.
Bref, en effet, la classe est à la fois un iterateur, mais aussi un accesseur ...
Au niveau de l'iterateur, je vois pas trop le soucis. En fait, parcourir mon objet revient au final à parcourir le tableau d'élement ... Donc je pense que les méthodes sont appropriées ... Non ?
LocalStone
mercredi 28 novembre 2007 à 00:32:22 | Re : Interface Iterator et problème de conception.

malalam

Administrateur CodeS-SourceS
Hello,

ce n'est pas la clef d'un itérateur qui ne peut pas être un objet : c'est la clef d'un tableau. Nuance. Tu auras donc exactement le même problème avec ton code, puisque tu utilises...un tableau.
Si tu veux vraiment aller vers là -je ne sais pas si c'est possible, et il est un peu tard pour que j'essaye là, même à la va vite-, tu dois implémenter Iterator et ArrayAccess. Ce sont des interfaces. Et gérer ton "tableau" différemment, sans utiliser un vrai tableau.  Il y a plein de possibilité en fait, mais il faut tester la plus efficace (et celle qui marche lol).

mercredi 28 novembre 2007 à 00:40:46 | Re : Interface Iterator et problème de conception.

LocalStone

Ralalala ... Bon, vous ne me laissez pas le choix. En fait, le problème que je présente là, je l'ai rencontré lors du codage de ces classes :
<?php
/* -------------------- */
   class Map implements Iterator, Countable {
  
      private $Keys = array();
     
      private $Values = array();
     
      public function put($mxdKey, $mxdValue) {
         if (!$this -> containsKey($mxdKey)) {
            array_push($this -> Keys, $mxdKey);
            array_push($this -> Values, $mxdValue);
         } else {
            $this -> Values[$this -> getKeyIndex($mxdKey)] = $mxdValue;
         }
      }
     
      private function getKeyIndex($mxdKey) {
         $intIndex = array_search($mxdKey, $this -> Keys);
         return ($intIndex === false) ? -1 : $intIndex;
      }
     
      public function containsKey($mxdKey) {
         return in_array($mxdKey, $this -> Keys);
      }
     
      public function containsValue($mxdValue) {
         return in_array($mxdValue, $this -> Values);
      }
     
      public function getValue($mxdKey) {
         $intIndex = $this -> getKeyIndex($mxdKey);
         return ($intIndex < 0) ? null : $this -> Values[$intIndex];
      }
     
      public function removeValue($mxdKey) {
         //echo 'Map::removeValue(' . $mxdKey . ')';
         $intKeyIndex = $this -> getKeyIndex($mxdKey);
         if ($intKeyIndex != -1) {
            unset($this -> Keys[$intKeyIndex]);
            //prev($this -> Keys);
            //$this -> Keys = array_values($this -> Keys);
            unset($this -> Values[$intKeyIndex]);
            //prev($this -> Values);
            //$this -> Values = array_values($this -> Values);
         }
      }
     
      private function getEntryByIndex($intIndex) {
         //echo 'Map::getEntryByIndex(' . (string)$intIndex . ') == ' . new MapEntry($this -> Keys[$intIndex], $this -> Values[$intIndex], $this) . '<br />';
         return new MapEntry($this -> Keys[$intIndex], $this -> Values[$intIndex], $this);
      }
     
      public function current() {
         $objEntry = $this -> getEntryByIndex($this -> key());
         //echo 'Map::current() == ' . $objEntry . '<br />';
         return $objEntry;//$this -> getEntryByIndex($this -> key());
      }
     
      public function key() {
         return key($this -> Values);
      }
     
      public function next() {
         //next($this -> Keys);
         next($this -> Values);
      }
     
      public function rewind() {
         //reset($this -> Keys);
         reset($this -> Values);
      }
     
      public function valid() {
         return current($this -> Values) !== false;
      }
     
      public function __toString() {
         $arrEntries = $this -> getEntries();
         return "{\n\t" . implode(", \n\t", $arrEntries) . "\n}";
      }
     
      public function getEntries() {
         $arrEntries = array();
         foreach ($this as $objEntry) {
            array_push($arrEntries, $objEntry);
         }
         return $arrEntries;
      }
     
      public function count() {
         return count($this -> Values);
      }
     
      public function getKeys() {
         return new Set(
            array_values(
               $this -> Keys
            )
         );
      }
     
      public function getValues() {
         return new Set(
            array_values(
               $this -> Values
            )
         );
      }
     
   }
/* -------------------- */
?>

Je voulais éviter de les poster parce que le code était trop long ... Mais le problème est le même, lors de la suppression d'un élement, lors d'un foreach, le parcour part en freestyle, et ça, bah ça va pas du tout ...
Bon, je vais me coucher aussi ... Trop prise de tête ! Mais j'espère que l'on pourra continuer l'échange dès demain !
LocalStone
mercredi 28 novembre 2007 à 01:24:50 | Re : Interface Iterator et problème de conception.

malalam

Administrateur CodeS-SourceS
Hello,

avec le foreach() tu ne pourras sans doute jamais : l'offset (foreach($array as $offset => $value) ne peut pas être un objet, à priori.
Mais tu peux quand même, foreach() mis à part (mais je peux me tromper sur ce point hein) simuler largement un tableau.
J'ai repris ton principe en le simplifiant un peu, avec des exemples (ouais finalement  je ne me suis pas couché de suite donc j'ai décidé de te montrer un peu ;-) ) :
<?php

class Map implements Iterator, ArrayAccess, Countable {
    protected $aKeys = array();
    protected $aValues = array();
    protected $iPos;
   
    public function __construct() {
        $this->rewind();   
    }
   
    public function append($offset, $value) {
        $this->aKeys[] = $offset;
        $this->aValues[] = $value;
        return true;
    }
   
    public function offsetExists($offset) {
        return in_array($mKey, $this->aKeys);
    }
   
    public function offsetGet($offset) {
        if(false !== ($mFound = array_search($offset, $this->aKeys))) {
            return $this->aValues[$mFound];
        }
        return false;
    }
   
    public function offsetSet($offset, $value) {
        $this->aKeys[] = $offset;
        $this->aValues[] = $value;
        return true;
    }
   
    public function offsetUnset($offset) {
        if(false !== ($mFound = array_search($offset, $this->aKeys))) {
            unset($this->aValues[$mFound]);
            unset($this->aKeys[$mFound]);
            return true;
        }
        return false;
    }
   
    public function current() {
        return $this->aValues[$this->iPos];
    }
   
    public function key() {
        return $this->aKeys[$this->iPos];
    }
   
    public function next() {
        $this->iPos ++;
    }
   
    public function rewind() {
        $this->iPos = 0;
    }
   
    public function valid() {
        return isset($this->aKeys[$this->iPos]);
    }
    public function count() {
        return count($this->aKeys);
    }
}

class A {
    public function say() {
        return 'I am a A<br />';
    }
   
    public function __toString() {
        return $this->say();
    }
}

class B {
    public function say() {
        return 'I am a B<br />';
    }
    public function __toString() {
        return $this->say();
    }
}

$map = new Map;
$Ak = new A;
$Bv = new B;
$Av = new A;
$Bk = new B;
$map->append($Ak, $Bv);
$map->append($Bk, $Av);
echo $map[$Ak];
echo '<hr />';
while($map->valid()) {
    echo $map->key()->say();
    echo $map->current()->say();
    $map->next();
}
?>

mercredi 28 novembre 2007 à 01:31:57 | Re : Interface Iterator et problème de conception.

neigedhiver

@Malalam : arrête-moi (si tu peux... non c'est un film ça) si je me trompe, mais l'idée ne serait-elle pas de wrapper l'objet à itérer avec une classe qui permet de simuler un ArrayAccess ?
mercredi 28 novembre 2007 à 01:42:13 | Re : Interface Iterator et problème de conception.

malalam

Administrateur CodeS-SourceS
C'était mon idée première, mais là j'ai calqué son code.
D'ailleurs j'ai oublié un truc dans mon code, essentiel. C'est peut-être le problème rencontré par LocalStone d'ailleurs : quand on supprime une valeur, il FAUT réindexer les 2 tableaux (et de la même manière), parce que sinon on a un trou, et on ne peut plus itérer correctement, forcément, je viens de voir que j'avais oublié ça en relisant mon code. Mais ce n'est pas sorcier, il suffit d'une petite méthode dédiée, pour faire ça.
mercredi 28 novembre 2007 à 10:55:46 | Re : Interface Iterator et problème de conception.

LocalStone

Pour les offset objet, j'ai abandonné l'idée. Du coup, à la place, le Map::current() retourne une instance de la classe MapEntry qui contient 2 méthodes : MapEntry::getKey() et MapEntry::getValue() qui font ... Bah je vous laisse deviner :P
Ok ... Donc au final, si j'ai bien compris, il ne faut pas que j'utilise les pointeurs interne d'un tableau pour itérer la map, mais que j'utilise un attribut spécial qui stocke l'état d'itération (l'équivalent de ton $iPos, Malalam). C'est bien ça ?

LocalStone

1 2

Cette discussion est classée dans : problème, classe, clé, interface, iterator


Répondre à ce message

Sujets en rapport avec ce message

[SPL] Une variable objet comme clé de l'interface Iterator [ par LocalStone ] Salut à tous, Alors voilà ma question : est-il possible d'utiliser l'interface Iterator (ou tout interface qui permettent le parcours d'un objet avec Méthodologie de codage ... [ par LocalStone ] Salut à tous ! Alors voilà, j'ai un petit problème de méthodologie ... Je m'explique : Imaginons que l'on ait une classe Cookie, qui modélise - comme Interface et __toString() [ par syndrael ] Savez vous s'il est possible d'utiliser __toString() dans une classe qui utilise une interface ?Mon problème est que j'obtiens suite à mon __toString( Problème avec les quotes [ par Toya78 ] Bonjour,je suis chargé depuis quelques mois de la réalisation du site Web d'une association.J'ai créé une interface d'administration qui permet de met Problème variable externe a une classe [ par destiny ] Bonjour,Ma question est simple, comment recuprer dans une classe un variable constante ? Exemple :define("DEFAULT_PAGE", "welcome");class initPage { Problème d'insertion de clé étrangère [ par samir1988 ] BonjourDans ma base de données, j'ai une table 'temperature' et une table 'enceinte'.Dans la table 'enceinte' j'ai une clé primaire appelée : 'idencei N'êtes vous tous pas suffisamment costaud en PHP ? [ par badjouane ] Pour solutionner mon problème ?Désolé, mais il me fallait un titre accrocheur et provocateur comme celui-la, pour être certain d'obtenir une certaine formation clé primaire [ par didou8513 ] Bonjour, J'espère que je vais ètre assez clair sur mon problème car c'est assez difficile à expliquer.Je possède une base de données oracle et je dois Problème de variables [ par WaNoU ThE EpSyLoN ] Bonjours à tous et à toutes,Comment faire pour qu'une variable définie dans une méthode d'une classe soit utilisable dans une autre méthode de cette m Concaténation d'entier pour clé primaire... [ par stevenleferran ] Bonjour, voilà mon problème :- chaque client a un numéro unique (100 pour client A , 200 pour client B...).- pour chaque demande d'un client, lors de


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,359 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales