====== Devweb 105 : Programmation Orientée Objet (POO) ====== ===== Introduction ===== En général, vous n'avez jusqu'à maintenant utilisé une **représentation procédurale** pour programmer, càd que vous aviez d'un côté vos données (listes, BDD...) et de l'autre côté une liste de fonctions à appliquer à ces données. Il existe une autre représentation très courante pour programmer appelée la **POO** (ou Programmation Orientée Objet). Lorsque l'on programme en utilisant la POO, tout notre code devient un ensemble d'objets qui interagissent entre eux. Chaque objet est défini selon des caractéristiques et un plan bien précis. En POO, ces informations sont contenues dans ce qu'on appelle des **classes**. ===== Les classes ===== Une classe est un ensemble d'attributs (~de caractéristiques) et de méthodes (~de fonctions). On peut donner l'exemple d'un **Personnage** de jeu vidéo qui aurait certains attributs (sa vie, son emplacement, son expérience, sa force...) et certaines méthodes (frapper, gagner de l'expérience, se déplacer...). Notre classe serait donc ainsi : {{ :formations:classe_personnage.png?400 |}} L'un des avantages de la POO est que l'on peut masquer le code à l'utilisateur. En général les **attributs** sont **privés** et les **méthodes** sont **publiques**. Il existe des méthodes permettant à l'utilisateur de modifier (**setter**) ou de consulter (**getter**) les attributs mais il ne peut qu'exécuter ces méthodes sans y avoir un accès direct. (Nous y reviendrons don't worry :p ) ==== Créer un classe en PHP ==== === Syntaxe de Base === La syntaxe de base est très simple : === Visibilité d'un attribut ou d'une méthode === A cela on va ajouter la déclaration des attributs et des méthodes, en les précédant de leur visibilité. Comme je viens de vous l'expliquer avant, il y a 2 types de visibilité : **public** et **private**. Si un attribut/une méthode est //**public**, alors on pourra y avoir accès depuis n'importe où depuis l'extérieur ou l'intérieur de l'objet//. Si un attribut/une méthode est //**private**, alors on pourra y avoir accès uniquement depuis l'intérieur de l'objet afin de nourrir nos méthodes//. Ceci s'appelle le principe d'encapsulation, c'est de cette manière qu'on peut interdire l'accès à nos attributs. === Création d'attributs === Comme je vous l'ai dit précédemment on va rendre les attribut privés et créer des méthodes publiques grâce auxquelles y accéder : Vous pouvez également initialiser les attributs lorsque vous les déclarez : Et son équivalent en **Java**, pour ceux qui y ont déjà touché en cours d'informatique : package com.devweb105; class Personnage { private int force = 50; private String localisation = “Lyon”; private int experience = 1; private int degat = 0; } === Création des méthodes === Comme je vous l'ai dit précédemment on va rendre les méthodes publiques afin d’accéder aux attributs, mais aussi de donner des actions aux Personnages : _localisation = $lieu; } public function frapper($personnage) // Une méthode qui frappera un personnage (suivant la force qu'il a). { $personnage->_vie = $personnage->_vie - $this->_degats; } public function gagnerExperience($xp) // Une méthode augmentant l'attribut $experience du personnage. { $this->_experience = $this->_experience + $xp; } //+ getters et setters (cf plus tard) } ==== Utiliser la classe ==== === Créer un objet === Syntaxe très simple grâce au mot-clé **new**: Soit, en java Personnage perso = new Personnage; === Appeler les méthodes de l'objet === Pour appeler une méthode d'un objet, il va falloir utiliser l'opérateur -->. Cela donne : === Ajouter les getters et les setters ainsi que le constructeur === Cela donne donc avec les getters et les setters et le constructeur: setForcePerso($force); $this->setNom($nom); $this->setNiveau(0); } // Liste des getters public function id() { return $this->_id; } public function nom() { return $this->_nom; } public function forcePerso() { return $this->_forcePerso; } public function degats() { return $this->_degats; } public function niveau() { return $this->_niveau; } public function experience() { return $this->_experience; } // Liste des setters public function setId($id) { // On convertit l'argument en nombre entier. // Si c'en était déjà un, rien ne changera. // Sinon, la conversion donnera le nombre 0 (à quelques exceptions près, mais rien d'important ici). $id = (int) $id; // On vérifie ensuite si ce nombre est bien strictement positif. if ($id > 0) { // Si c'est le cas, c'est tout bon, on assigne la valeur à l'attribut correspondant. $this->_id = $id; } } public function setNom($nom) { // On vérifie qu'il s'agit bien d'une chaîne de caractères. if (is_string($nom)) { $this->_nom = $nom; } } public function setForcePerso($forcePerso) { $forcePerso = (int) $forcePerso; if ($forcePerso >= 1 && $forcePerso <= 100) { $this->_forcePerso = $forcePerso; } } public function setDegats($degats) { $degats = (int) $degats; if ($degats >= 0 && $degats <= 100) { $this->_degats = $degats; } } public function setNiveau($niveau) { $niveau = (int) $niveau; if ($niveau >= 1 && $niveau <= 100) { $this->_niveau = $niveau; } } public function setExperience($experience) { $experience = (int) $experience; if ($experience >= 1 && $experience <= 100) { $this->_experience = $experience; } } } ?> Le même code, en Java donnerait : Un Getter public String[] getNom() { return this.nom; } Un Setter public void setNom(String[] nom) { this.nom = nom; } === Créer une classe qui hérite d'une autre classe === Dans cet exemple, on crée une classe Archer qui hérite de la classe personnnage. Ici, les archers ont leurs dégats initialisés à 10. Et leur nom contient le préfixe 'Archer '. class Archer extends Personnage{ public function __construct($force, $nom){ parent::__construct($force, $nom); $this->setDegats(10); } public function setNom($cible){ parent::setNom($cible); $this->_nom = 'Archer '.$this->nom(); } } ===== Exercices ===== ==== Exercice 1 - Calculette peu performante ==== Cet exercice a pour but de voir si vous avez compris les bases avant de passer à la suite On vous demande ici de créer une classe MyCalculator toute simple qui prend 2 nombres en arguments. Vous devrez créer une méthode afficher la somme de ces deux nombres, une autre pour afficher la multiplication de ces deux nombres. On doit donc obtenir quelque chose qui resemble à : add()."
"; // Affiche 18 echo $mycalc->multiply()."
"; // Affiche 72 ?>
==== Exercice 2 - Comptes Bancaires ==== === Introduction === Le but de cet exercice est de modéliser le système bancaire de manière assez simple. Ce système bancaire fait par vos soins devra comprendre plusieurs banques, permettre à des gens d'ouvrir un compte dans une banque de leur choix, de pouvoir faire des transactions bancaires ainsi que de pouvoir ajouter ou retirer de l'argent de leur compte. === Exigences === Votre code devra comprendre au début 3 classes : * La classe Banque qui contient au moins les attributs nom, nombre de clients; * La classe Client qui contient au moins les attributs nom, prenom, compte; * La classe Compte qui contient au moins les attributs client, banque, montant; On veut que: - Un client puisse créer un compte dans la banque de son choix - Un client puisse ajouter ou retirer de l'argent de son compte bancaire - Un client puisse faire un virement à un autre client (Pour simplifier chaque client possède un seul compte à une seule banque) - Un client ne puisse pas être en négatif - Lorsqu'une transaction a lieu entre deux clients de banques différentes, il y ait 10% de frais supplémentaires pour celui qui fait le virement Nous vous suggérons d'implémenter ces fonctionnalités une par une et de les tester avant de passer à la suivante. Vous mettrez tout les attibuts en privé et utiliserez des getters et setters pour y accéder et les modifier depuis les autres classes. Les méthodes et attributs seront nommées en CamelCase (ex: NomDeLaMéthode). Bonus: Vous pouvez ajouter une classe Devise et modifier les autres classes pour permettre a des clients d'ouvrir des comptes dans plusieurs devises et faire des transaction entre comptes avec des devises différentes (Attention aux conversions !) === Format attendu === Pour pouvoir tester facilement votre code, il va falloir respecter certaines notations pour utiliser ce fichier: getNom()." a ".$banque->getNombreClients()." clients
"; // KSI a 0 clients $banque->createCompte($client); echo $banque->getNom()." a ".$banque->getNombreClients()." clients
"; // KSI a 1 clients $compte = $client->getCompte(); echo $client->getNom()." ".$client->getPrenom()." possède un compte chez ".$compte->getBanque()->getNom()."
"; // Jean Loutre possède un compte chez KSI echo "Ce client a ".$compte->getMontant()."€
"; // Ce client a 0€ // Question 2 $compte->ajouter(100); echo "Ce client a ".$compte->getMontant()."€
"; // Ce client a 100€ $compte->retirer(25); echo "Ce client a ".$compte->getMontant()."€
"; // Ce client a 75€ // Question 3 $client2 = new Client("Jeannot", "Loutre Junior"); $banque->createCompte($client2); $client->virer($client2, 25); echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 50€ echo $client2->getNom()." ".$client2->getPrenom()." possède ".$client2->getCompte()->getMontant()."€
"; // Jeannot Loutre Junior possède 25€ // Question 4 $compte->retirer(100); // Cette ligne doit renvoyer une alerte et ne pas réaliser la transaction $client->virer($client2, 100); // Cette ligne doit renvoyer une alerte et ne pas réaliser la transaction echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 50€ echo $client2->getNom()." ".$client2->getPrenom()." possède ".$client2->getCompte()->getMontant()."€
"; // Jeannot Loutre Junior possède 25€ // Question 5 $banque2 = new Banque("FOCEEN"); $client3 = new Client("Jeanne", "Loutréa"); $banque2->createCompte($client3); $client->virer($client3, 10); echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 39€ echo $client3->getNom()." ".$client3->getPrenom()." possède ".$client3->getCompte()->getMontant()."€
"; // Jeanne Loutréa possède 10€
==== Exercice 3 - Banque en ligne ==== Il est nécessaire d'avoir fait l'exercice 2 pour réaliser celui-ci. L'objectif de cet exercice est de vous familiariser avec la notion d'héritage. Pour cela, il vous est demandé d'implémenter une classe BanqueEnLigne qui hérite de la classe Banque qui permettra de modéliser une banque en ligne. On veut que : - La banque en ligne respecte le cahier des charges de la banque classique sans la partie concernant les transactions (cf question 3) - La banque en ligne doit en plus avoir un attribut 'url' donnant l'url de son site web. - Les transactions réalisées par les clients des banques en ligne n'engendrent aucun frais Dans cet exercice, il est **interdit de modifier le code de l'exercice 2**. Les consignes et conseils de l'exercice 2 restent tous valides. Voici un fichier à utiliser pour tester votre code. getNom()." a ".$banque->getNombreClients()." clients
"; // KSI a 0 clients $banque->createCompte($client); echo $banque->getNom()." a ".$banque->getNombreClients()." clients
"; // KSI a 1 clients $compte = $client->getCompte(); echo $client->getNom()." ".$client->getPrenom()." possède un compte chez ".$compte->getBanque()->getNom()."
"; // Jean Loutre possède un compte chez KSI echo "Ce client a ".$compte->getMontant()."€
"; // Ce client a 0€ // Question 1.2 (Ancienne question 2) $compte->ajouter(100); echo "Ce client a ".$compte->getMontant()."€
"; // Ce client a 100€ $compte->retirer(25); echo "Ce client a ".$compte->getMontant()."€
"; // Ce client a 75€ // Question 1.3 (Ancienne question 3) $client2 = new Client("Jeannot", "Loutre Junior"); $banque->createCompte($client2); $client->virer($client2, 25); echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 50€ echo $client2->getNom()." ".$client2->getPrenom()." possède ".$client2->getCompte()->getMontant()."€
"; // Jeannot Loutre Junior possède 25€ // Question 1.4 (Ancienne question 4) $compte->retirer(100); // Cette ligne doit renvoyer une alerte et ne pas réaliser la transaction $client->virer($client2, 100); // Cette ligne doit renvoyer une alerte et ne pas réaliser la transaction echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 50€ echo $client2->getNom()." ".$client2->getPrenom()." possède ".$client2->getCompte()->getMontant()."€
"; // Jeannot Loutre Junior possède 25€ // Question 2 $banque->setUrl("https://ksi-centrale-marseille.fr/"); echo "L'url de ".$banque->getNom()." est ".$banque->getUrl()."
"; // L'url de KSI est https://ksi-centrale-marseille.fr/ // Question 5.1 $banque2 = new Banque("FOCEEN"); $client3 = new Client("Jeanne", "Loutréa"); $banque2->createCompte($client3); $client->virer($client3, 20); echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 30€ echo $client3->getNom()." ".$client3->getPrenom()." possède ".$client3->getCompte()->getMontant()."€
"; // Jeanne Loutréa possède 20€ // Question 5.2 (Ancienne question 5) $client3->virer($client2, 10); echo $client->getNom()." ".$client->getPrenom()." possède ".$compte->getMontant()."€
"; // Jean Loutre possède 40€ echo $client3->getNom()." ".$client3->getPrenom()." possède ".$client3->getCompte()->getMontant()."€
"; // Jeanne Loutréa possède 9€
===== Compléments ===== ==== Les héritages ==== L'héritage en POO (que ce soit en C++, Java ou autre langage utilisant la POO) est une technique très puissante et extrêmement pratique. Ce chapitre sur l'héritage est le chapitre à connaitre par cœur (ou du moins, le mieux possible). Pour être bien sûr que vous ayez compris le principe, un TP vous attend au prochain chapitre. ;) Allez, j'arrête de vous mettre la pression, allons-y ! Notion d'héritage Définition Quand on parle d'héritage, c'est qu'on dit qu'une classe B hérite d'une classe A. La classe A est donc considérée comme la classe mère et la classe B est considérée comme la classe fille. Concrètement, l'héritage, c'est quoi ? Lorsqu'on dit que la classe B hérite de la classe A, c'est que la classe B hérite de tous les attributs et méthodes de la classe A. Si l'on déclare des méthodes dans la classe A, et qu'on crée une instance de la classe B, alors on pourra appeler n'importe quelle méthode déclarée dans la classe A du moment qu'elle est publique. Schématiquement, une classe B héritant d'une classe A peut être représentée comme ceci (figure suivante). {{ :formations:image.png?400 |}} Quand est-ce que je sais si telle classe doit hériter d'une autre ? Soit deux classes A et B. Pour qu'un héritage soit possible, il faut que vous puissiez dire que A est un B. Par exemple, un magicien est un personnage, donc héritage. Un chien est un animal, donc héritage aussi. Bref, vous avez compris le principe. ;) ==== La Modèle-Vue-Contrôleur (MVC) ==== {{ :formations:mvc.jpeg?400 |}} Un des plus célèbres design patterns s'appelle MVC, qui signifie Modèle - Vue - Contrôleur. C'est celui que nous allons découvrir maintenant. Le pattern MVC permet de bien organiser son code source. Il va vous aider à savoir quels fichiers créer, mais surtout à définir leur rôle. Le but de MVC est justement de séparer la logique du code en trois parties que l'on retrouve dans des fichiers distincts. **Modèle** : cette partie gère les données de votre site. Son rôle est d'aller récupérer les informations « brutes » dans la base de données, de les organiser et de les assembler pour qu'elles puissent ensuite être traitées par le contrôleur. On y trouve donc entre autres les requêtes SQL. **Vue** : cette partie se concentre sur l'affichage. Elle ne fait presque aucun calcul et se contente de récupérer des variables pour savoir ce qu'elle doit afficher. On y trouve essentiellement du code HTML mais aussi quelques boucles et conditions PHP très simples, pour afficher par exemple une liste de messages. **Contrôleur** : cette partie gère la logique du code qui prend des décisions. C'est en quelque sorte l'intermédiaire entre le modèle et la vue : le contrôleur va demander au modèle les données, les analyser, prendre des décisions et renvoyer le texte à afficher à la vue. Le contrôleur contient exclusivement du PHP. C'est notamment lui qui détermine si le visiteur a le droit de voir la page ou non (gestion des droits d'accès). Remarque : Cette forma a été fait à la va-vite et est donc temporairement grandement inspirée de la génialissime et exhaustive formation sur la POO d'OpenClassroom : https://openclassrooms.com/fr/courses/1665806-programmez-en-oriente-objet-en-php/1666060-utiliser-la-classe ---- Cette formation a été réalisée par [[user:amenasria|Alexandre Menasria]], [[user:rgrondin|Romain GRONDIN]] et [[user:aiksi|Ahmad IKSI]].