JeuWeb - Crée ton jeu par navigateur
PHP: instanceof - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : PHP: instanceof (/showthread.php?tid=7379)

Pages : 1 2 3


PHP: instanceof - Xenos - 06-06-2015

Je pense qu'il serait judicieux de séparer cette discussion de celle de East.

Je vous propose cette petite successions de code. J'aimerai que chacun me dise à quel moment il pense qu'on a quitté "la bonne façon de faire". Pour rappel, je ne parle plus de East, mais bien d'instanceof.


Code PHP :
<?php 
public function giveGold(Gold $x) {
$this->gold += $x->getValue();
}
On va dire que c'est le code 0. Je file du Gold à ma classe.



Etape 1: mon algo interne change, si c'est un gros paquet d'or, je dis "youpi"
Code PHP :
<?php 
public function giveGold(Gold $x) {
$this->gold += $x->getValue();
if (
$x->getValue() > 100)
echo
"Youpi!";
}



Etape 2: finalement, je jette les petits paquets d'or par terre, et ne garde que les gros
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getValue() > 100) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
else
$x->estJeteParTerre();
}



Etape 3: bof en fait, c'est fatigant de jeter un truc par terre, je vais juste l'ignorer
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getValue() > 100) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}



Etape 4: en fait, j'aime bien l'or, je ne vais pas placer ma tolérance à 100, mais à 0 (des sacs non-vides donc)
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getValue() > 0) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}



Etape 5: ah le langage a changé d'opérateur; > s'appelle maintenant greaterThan (il fait pareil, seul son nom change)
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getValue() greaterThan 0) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}



Etape 6: Gold a été changée, et maintenant, la classe retourne un Nombre, qui définit "greaterThan". De plus, ce retour implémente NombreNegatif, NombrePositif, ou NombreNul.
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getNombre()->greaterThan(0)) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}



Etape 7: Je me rends compte que Nombre implémente une méthode qui permet de savoir si c'est positif; utilisons-la
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getNombre()->isPositive()) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}



Etape 8: La méthode dans Nombre devient plus générique
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getNombre()->is('NombrePositif')) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}



Etape 9: PHP change sa syntaxe, et je peux maintenant me passer de la -> En plus, je peux me passer des guillemets. A part ça, c'est toujours un appel de méthode (un peu byzare comme syntaxe, mais bon)
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getNombre() is NombrePositif ) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}


Etape 10: Mais en fait, ce "is" existe déjà dans tout objet PHP et même pour toute variable PHP...
Code PHP :
<?php 
public function giveGold(Gold $x) {
if (
$x->getNombre() instanceof NombrePositif ) {
$this->gold += $x->getValue();
echo
"Youpi!";
}
}


Etape 11: Finalement au lieu d'accepter du Gold, j'accepte directement un Nombre
Code PHP :
<?php 
public function giveGold(Nombre $x) {
if (
$x instanceof NombrePositif ) {
$this->gold += $x;
echo
"Youpi!";
}
}

Là j'accepte donc tout objet Nombre en entrée, mais dans mon algo interne, je décide de réagir différemment suivant si c'est un NombrePositif.


RE: PHP: instanceof - Ter Rowan - 06-06-2015

moi je dirais que l'exemple est trop trivial (gold)

j'enverrai pas un gold, mais un int ou float

mais bon, supposons qu'on soit avec ce "gold"

alors je ne ferais pas du tout comme toi :

$this->gold devrait lui meme être un gold et pas un int

et donc

au lieu d'avoir $this->gold += $x->getValue on aurait

soit $this->gold += $x (en supposant qu'on puisse surcharger += pour une classe en particulier)
soit $this->gold->add ($x)


sinon plus factuellement
pour des cas de comparaison de nombre (genre ici >0) je prends le getter et je compare, et c'est tout
qu'est ce que je vais utiliser des concepts objets poussés pour au final me retrouver avec des notions d'arithmétique de base.

Tout redévelopper ? réinventer la roue ?

pour cela que je trouve l'exemple trop trivial, essaie avec des algo qui soient plus complexes (genre voiture sportive qui plaise à madame)


RE: PHP: instanceof - Xenos - 06-06-2015

Okay, voici donc un autre exemple moins trivial. Je vais un peu changer d'approche (cela ferait trop d'étapes pour un exemple moins trivial). Je donne une spec, j'en propose l'implé qui me semble cohérente, et j'aimerai savoir comment vous l'implémenteriez:

Irma, la classe des voyantes, ne peut qu'acheter des Voiture. Elle ne l'achète que si c'est une Ferrari rouge.

Code PHP :
<?php 
interface Voiture {
public function
getColor();
}
interface
Ferrari {
}
class
F50 implements Voiture, Ferrari {
private
$color;
public function
__construct($color) {
$this->color = (string)$color;
}
}
class
Irma {
public function
acheter(Voiture $voiture) {
if (
$voiture instanceof Ferrari && $voiture->getColor() == "rouge") {
echo
"J'achète!";
}
}
}
Est-ce que ça vous semble cohérent? Si non, pourquoi? Comment vous l'implémenteriez?


RE: PHP: instanceof - srm - 07-06-2015

Il n'y a pas besoin de instanceof selon moi.
Puisqu'une Marque n'a pas de comportement elle a aucun intérêt à être dans une interface.


<?php

namespace Voiture;

interface Voiture
{
   public function jeVeuxConnaitreTaCouleurEtTaMarque(Acheteur $acheteur);
}

interface Acheteur
{
   public function acheter(Voiture $voiture);
   public function laMarqueEtLaCouleurDeLaVoitureSont(
       Marque $marque,
       Couleur $couleur,
       Voiture $voiture
   );
}


final class Marque
{
   private $pattern = '/^[A-Z][a-z]*$/';

   /**
    * @params string
    */
   public function __construct($value)
   {
       if (is_string($value) === false) {
           throw new \RuntimeException('Marque should be construct from a string');
       }

       if (strlen($value) === 0) {
           throw new \RuntimeException('Marque length should be greater than 0');
       }

       if (preg_match($this->pattern, $value) !== 1) {
           throw new \RuntimeException('Marque should match the pattern ' . $this->pattern);
       }

       $this->value = $value;
   }

   public function equals(Couleur $couleur)
   {
       return $this->value === $couleur->value;
   }
}


final class Couleur
{
   private $pattern = '/^[a-z]*$/';

   /**
    * @params string
    */
   public function __construct($value)
   {
       if (is_string($value) === false) {
           throw new \RuntimeException('Couleur should be construct from a string');
       }

       if (strlen($value) === 0) {
           throw new \RuntimeException('Couleur length should be greater than 0');
       }

       if (preg_match($this->pattern, $value) !== 1) {
           throw new \RuntimeException('Couleur should match the pattern ' . $this->pattern);
       }

       $this->value = $value;
   }

   public function equals(Couleur $couleur)
   {
       return $this->value === $couleur->value;
   }
}

namespace Application;


class F50 implements \Voiture\Voiture
{
   private $couleur;
   private $marque;

   public function __construct(\Voiture\Couleur $couleur)
   {
       $this->couleur = $couleur;
       $this->marque = new \Voiture\Marque('Ferrari');
   }

   public function jeVeuxConnaitreTaCouleurEtTaMarque(\Voiture\Acheteur $voiture)
   {
       $voiture->laMarqueEtLaCouleurDeLaVoitureSont($this->marque, $this->couleur, $this);

       return $this;
   }
}

class Irma implements \Voiture\Acheteur
{
   private $voitureCouleur;
   private $voitureMarque;

   public function acheter(\Voiture\Voiture $voiture)
   {
       $irma = new Irma();
       $voiture->jeVeuxConnaitreTaCouleurEtTaMarque($irma);
       if (
           $irma->voitureCouleur == new \Voiture\Couleur('rouge')
           && $irma->voitureMarque == new \Voiture\Marque('Ferrari')
       ) {
           echo "J'achète\n";
       }

       return $this;
   }

   public function laMarqueEtLaCouleurDeLaVoitureSont(
       \Voiture\Marque $marque,
       \Voiture\Couleur $couleur,
       \Voiture\Voiture $voiture
   ) {
       $this->voitureMarque = $marque;
       $this->voitureCouleur = $couleur;

       return $this;
   }
}

$f50 = new F50(new \Voiture\Couleur('rouge'));
$irma = new Irma();
$irma->acheter($f50);



RE: PHP: instanceof - Xenos - 08-06-2015

Pourquoi une marque n'aurait-elle pas de comportement? Ou, autrement formulé, pourquoi un comportement (comme passer la 7e vitesse) ne pourrait pas être propre à une marque?
Après, l'interface Voiture::jeVeuxConnaitreTaCouleurEtTaMarque(Acheteur $acheteur) force toute voiture à implémenter cette méthode, qu'elle ait ou non une amrque/couleur (et le nom de méthode interdit d'avoir plusieurs marques).


PS: comme la dernière fois, tu as oublié $irma->voitureCouleur = null; $irma->voitureMarque = null .


RE: PHP: instanceof - srm - 08-06-2015

Vu que je fais un new Irma je n'ai pas besoin de forcer à null, dans une nouvelle instance il l'est déjà Smile

Bah une marque c'est un valueObject pour moi.
Une marque n'a pas de comportement.
Et si on se dit que la Marque définis le nombre de vitesse, ça n'est toujours pas un comportement mais une caractéristique.

J'ai montré volontairement avec jeVeuxConnaitreTaCouleurEtTaMarque une autre façon de passer les informations, tu peux te donner le droit de passer plusieurs informations à la fois.
Mais comme tu l'as soulevé, c'est moins souple.
C'est un choix à faire à la conception.


RE: PHP: instanceof - Xenos - 08-06-2015

Je ne parlais pas de passerLa7eVitesse() comme un nombre de vitesses, mais bien comme un comportement, comme par exemple allumerLeLogoSurLeCapot() ou faireUnBruitDePantèreQuiRugit().
Si tu préfères, remplace la Voiture par le concept Vehicule, et considère que Irma n'achète que les Camion, et dit "crotte" pour le reste. Là, t'auras bien des comportement spécifiques (le Camion aura le comportement attacherRemorque() ou ouvrirPortieres() qu'une Moto n'aura pas).

Dans ce genre de cas, tu ferais comment?


RE: PHP: instanceof - Ter Rowan - 09-06-2015

je rejoignais srm

ca sert a rien de créer des interfaces / classes si il n'y a pas un vrai besoin

c'est le souci de tes exemples Xenos, ils sont perturbants.

Après oui si tu as besoin de classes (camion , moto) ou d'interface parce qu'il y a une vraie justification objet (comportement etc) alors oui, je pense qu'utiliser instance of est une bonne chose

maintenant définir tout comme étant des interfaces pour pouvoir faire if instanceof au lieu de if ->Color() == 'blue' ne me semble pas pertinent

tout n'est pas "type" ni "classe/interface"


moi je dis utilisons la richesse du langage sélectionné plutôt que de contraindre à une méthode pour utiliser cette méthode a tout prix


RE: PHP: instanceof - srm - 09-06-2015

Ton exemple n'est toujours pas très clair pour moi.
J'ai déjà une interface Voiture, donc pourquoi tu me parles de Moto ?
Je ne vais pas faire Moto extends Voiture.

Et si Irma doit pouvoir acheter une Moto je fais :
class Irma implements \Voiture\Acheteur, \Moto\Acheteur
public function acheterMoto(\Moto\Moto $moto)


RE: PHP: instanceof - Xenos - 09-06-2015

Code PHP :
<?php 
interface Vehicule {
public function
getColor();
}
interface
Voiture extends Vehicule {
public function
circulerEnVille();
}
interface
Camion extends Vehicule {
public function
attacherRemorque();
}
interface
Moto extends Vehicule {
public function
roulerSurRoueArriere();
}
class
AmericanTruck implements Camion {
}
class
Davidson implements Moto {
}

class
Irma {
public function
acheterVehicule(Vehicule $vehicule) {
if (
$vehicule instanceof Camion)
echo
"Okay I buy it";
else
echo
"I'll only buy it for half its price";
}
}

Ce genre d'exemple est-il plus clair?