JeuWeb - Crée ton jeu par navigateur
Article Passer d'un code procédural à la programmation orientée objet (POO) - 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 : Article Passer d'un code procédural à la programmation orientée objet (POO) (/showthread.php?tid=8211)



Passer d'un code procédural à la programmation orientée objet (POO) - Xenos - 14-09-2020

Passer d'un code procédural à la programmation orientée objet (POO)

Ce tutorial est grandement basé sur les échanges de ce sujet du forum :

[special thanks aux participants pour leurs questions et à ceux qui ont pris le temps de répondre]

Objectifs

Ce tutorial a pour objectifs de :
  • donner l'envie de passer à la programmation orientée objet (POO)

  • expliquer par des exemples concrêts les avantages de la POO

  • montrer des pièges à éviter et de bonnes pratiques OO


On ne parlera pas en détail de :
  • la syntaxe compléte et précise de la POO en PHP

  • les concepts avancés de la POO ( héritage, design patterns… )


Le but est de motiver à la POO, la technique se trouvera dans d'autres tutoriaux.

Introduction

On entend souvent parler de la POO en termes positifs :
  • ça fait du code propre

  • le code est facile à comprendre

  • le code est moins brouillon qu'en procédural

  • le code est facile à modifier


Facile à dire, mais concrêtement , comment on fait ? La première étape est l'utilisation de fonctions.

Utiliser des fonctions

C'est quoi une fonction ?

Une fonction est un petit bout de programme, avec un nom, que l'on peut utiliser et réutiliser dans un programme. On doit définir la fonction, ensuite on peut l'utiliser.

exemple :

function direBonjour( $prenom )
{
  echo "Bonjour, " . $prenom . " ! Content de te voir lire ce tutorial Smile";
}
 
// et on l'utilise comme ceci :
direBonjour('Corinne');
// et ça affiche le texte :
// Bonjour, Corinne ! Content de te voir lire ce tutorial Smile
 
// ou comme cela :
$prenom = 'Natalia';
direBonjour($prenom);
// et ça affiche le texte :
// Bonjour, Natalia ! Content de te voir lire ce tutorial Smile

Pourquoi et quand utiliser une fonction ?

On va utiliser les fonctions pour simplifier le code :

DRY ( Don't Repeat Yourself, ne recopie pas ton code ) :

Si on utilise un bout de code à plusieurs endroits d'une page, oubien sur plusieurs pages, alors on peut utiliser une fonction. Les avantages sont :
  • écrire moins de lignes : on a moins à taper, on ne recopie pas

  • lire moins de lignes : le code est plus court ex : on remplace 5 lignes pour se connecter à la base de données par une seule ligne

  • en cas de bug, on ne doit pas chercher parmi tous les fichiers où se répéte le code. On va directement dans la fonction, on la corrige et ça marchera partout, dans tous les autres fichiers utilisant la fonction. On ne risque pas d'oublier une correction dans un fichier au fin fond d'un dossier


Raccourcir le code :

Même si un code n'est pas répété à plusieurs endroits, créer une fonction est intéressant. Si vérifier les données pour un formulaire d'inscription prend 100 lignes, puis que l'on fait l'inscription ou un formulaire d'erreur selon le résultat, avec les 100 lignes de départ, on n'aura pas une vue d'ensemble du script.

Script sans fonctions, peu lisible car on a du code sur plusieurs écrans :

// script qui vérifie l'inscription
// code de vérification des données
// 100 lignes
if( isset($_POST['login'] )
{
// etc
}
// ...
// ...
// ...
// Puis la logique du script :
if( $erreur == FALSE )
{
  // on enregistre l'inscription
  // 30 lignes
}else{
  // on indique l'erreur
  // 50 lignes
}

Script avec fonctions, lisible car on a le script en quelques lignes, avec des noms de fonctions compréhensibles :

// on inclue le fichier où sont définies les fonctions
include('fonctions/inscription.php');
// code de vérification des données, renvoie TRUE ou FALSE
$erreur = verifierDonnees($login, $mail, $pass, $pass_confirmation);
if( $erreur == FALSE )
{
  // on enregistre l'inscription
  enregistrerInscription($login, $mail, $pass);
}else{
  // on indique l'erreur
  afficherErreur($login, $mail, $pass, $pass_confirmation);
}

Le code avec des fonctions est plus court et plus lisible que le code sans fonctions. On a une vision globale du script. Pour rentrer dans le détail, il faut consulter le fichier fonctions/inscription.php, qui contient le code technique des fonctions.

Le code avec fonctions ressemble fortement au pseudo code, traitant la logique du script :
  • vérifier les données

  • si il n'y a pas d'erreurs dans les données saisies

  • alors enregistrer l'inscription

  • sinon, afficher les erreurs


Avec les fonctions, on obtient 2 niveaux de débuggage :
  • les erreurs techniques, dans le code des fonctions

  • les erreurs logiques, dans la structure du script


Plus loin, plus beau que les fonctions : la POO

Modifier un code avec des fonctions : problèmes...

Avec les fonctions, on rend les scripts plus courts, plus lisibles, le code technique est centralisé, on corrige les bugs plus facilement.
Un problème se posera lorsqu'on fera évoluer le script.

Continuons sur le script d'inscription :

On gère les informations suivantes :
  • login

  • mail

  • pass

  • pass confirmation


On a fait des fonctions qui marchent bien, aux noms parlants :
  • verifierDonnees( $login, $mail, $pass, $pass_confirmation )

  • enregistrerCompte($login, $mail, $pass)

  • envoyerConfirmationMail($login, $mail)


On veut passer à la V2, et ajouter des races, à choisir à l'inscription. On doit modifier les fonctions :
  • verifierDonnees( $login, $mail, $pass, $pass_confirmation, $race)

  • enregistrerCompte($login, $mail, $pass, $race)

  • envoyerConfirmationMail($login, $mail, $race)


C'est facile, tout est dans le fichier fonctions/inscription.php . Mais on doit aussi modifier le script utilisant les fonctions : on doit ajouter partout $race aux arguments.

C'est facile si tout se trouve dans un seul script, mais :
  • et si ça se trouve sur plusieurs pages ?

  • comment être sûr de ne pas en oublier ? (cf ex de zamentur fonction placer() qui se retrouve partout )

  • comment être sûr qu'on ne va pas se planter dans un script et taper $rcae au lieu de $race ?


On a un code technique facile à corriger : les bugs sur la fonction sont à un seul endroit.

Mais le code est embêtant à faire évoluer : il faut parcourir tous les fichiers pour trouver toutes les utilisations de la fonction et les modifier à chaque fois. Pas très pratique…

Ca serait pratique de faire disparaitre les arguments des fonctions lorsqu'on les utilise, comme ça on ne devra pas modifier tous les scripts. C'est faisable grâce à la POO & l'encapsulation.

Faire "disparaître" les arguments des fonctions

L'idée derrière la POO & l'encapsulation, c'est de relier les données/ les variables et les fonctions.

Reprenons les fonctions d'inscription :
  • verifierDonnees( $login, $mail, $pass, $pass_confirmation, $race)

  • enregistrerCompte($login, $mail, $pass, $race)

  • envoyerConfirmationMail($login, $mail, $race)


Concernant les données, on a :
  • $login, $mail, $race qui sont utilisés dans les 3 fonctions

  • $pass dans 2 fonctions

  • $pass_confirmation dans une seule fonction


Ces données constitueront les membres ( ou attributs ) de la classe. Les membres d'une classe sont des variables qui stockent des valeurs.

class inscription 
{
// les membres de la classe
public $login;
public $mail;
public $pass;
public $pass_confirmation;
public $race;
}

Les fonctions seront des méthodes dans la classe. Elles utiliseront les valeurs stockées dans les membres à la place des arguments.

on aura donc :

class inscription 
{
// les membres de la classe
public $login;
public $mail;
public $pass;
public $pass_confirmation;
public $race;
 
 
public function verifierDonnees()
{
// code technique pour vérifier les données ici
}
 
public function enregistrerCompte()
{
// code technique pour enregistrer le compte ici
}
 
public function envoyerConfirmationMail()
{
// code technique pour envoyer la confirmation par mail ici
}
 
} // FIN de la classe inscription

Comment les méthodes accèdent-elles aux membres ?

En utilisant le $this :
  • $this→login représente le membre $login dans la classe

  • $this→mail représente le membre $mail dans la classe

  • etc


Il manque un point important : l'initialisation des membres. Comment attribuer des valeurs aux membres ? Via une méthode spéciale : le constructeur.

Une première approche est :

class inscription 
{
// les membres de la classe
// ...
 
// le constructeur, 1ere approche
public function __construct($login, $mail, $pass, $pass_confirmation, $race)
{
$this->login = $login;
$this->mail = $mail;
// etc
}
 
// les autres méthodes
// ...

Mais ça ne va pas : si on doit ajouter encore un membre à la classe, par exemple la date de naissance ou le pays, il faudra modifier les scripts partout où le constructeur est utilisé… on en revient au même qu'avec les fonctions.

La deuxième approche est l'utilisation d'un tableau pour initialiser les membres :

chaque index de tableau portera le nom d'un membre :

class inscription 
{
// les membres de la classe
// ...
 
// le constructeur, 2eme approche
public function __construct($tableau)
{
$this->login = $tableau['login'];
$this->mail = $tableau['mail'];
// etc
}
 
// les autres méthodes
// ...

Cette fois-ci, l'ajout d'un membre ne modifiera pas l'utilisation du script. On a gagné ! Smile

Pour l'ajout d'une donnée à l'inscription, on aura donc les modifications suivantes à effectuer :
  • ajout du champ dans le formulaire d'inscription

  • ajout du membre dans la classe

  • modification du fonctionnement interne des méthodes de la classe


et c'est tout !

Le script de traitement ne changera pas :

/* page de traitement du formulaire d'inscription */
 
// on inclue la classe
include('classes/inscription.php');
 
// on crée un objet à partir des données envoyées en post dans le tableau $_POST
$inscription = new inscription($_POST);
$erreur = $inscription->verifierDonnees();
if( $erreur == FALSE )
{
  // on enregistre l'inscription
  $inscription->enregistrerInscription();
  $inscription->envoyerConfirmationMail()
}else{
  // on indique l'erreur
  $inscription->afficherErreur();
}

Quelques pistes pour débuter en POO
  • initialiser un objet via un tableau en paramètre dans le constructeur

  • faire des méthodes unitaires, c'est à dire qui effectuent une seule action logique

  • faire des tests unitaires


Tout ceci constitue une première approche de la POO. On peut faire des choses plus poussées, des constructions spécifiques permettant de modifier et de faire vivre un code encore mieux que ce qui est présenté ici, mais c'est une autre histoire…

Quelques liens utiles
  • Le PHP Facile : Explications et exemples, avec bien sur une partie sur la POO.


écrit par Pascal, sur la base des éléments proposés par les camarades de jeuphp