Dans la programmation orientée objet, le polymorphisme est un outil puissant et fondamental. Il peut être utilisé pour créer un flux plus organique dans votre application. Ce tutoriel décrira le concept général de polymorphisme et expliquera comment le déployer facilement en PHP..
Polymorphisme est un long mot pour un concept très simple.
Le polymorphisme décrit un modèle de programmation orientée objet dans lequel les classes ont des fonctionnalités différentes tout en partageant une interface commune.
La beauté du polymorphisme réside dans le fait que le code travaillant avec les différentes classes n'a pas besoin de savoir quelle classe il utilise, car elles sont toutes utilisées de la même manière..
Une analogie du polymorphisme dans le monde réel est un bouton. Tout le monde sait utiliser un bouton: vous appliquez simplement une pression dessus. Cependant, ce qu'un bouton "fait" dépend de ce à quoi il est connecté et du contexte dans lequel il est utilisé - mais le résultat n'affecte pas la façon dont il est utilisé. Si votre patron vous demande d'appuyer sur un bouton, vous disposez déjà de toutes les informations nécessaires pour effectuer la tâche..
Dans le monde de la programmation, le polymorphisme est utilisé pour rendre les applications plus modulaires et extensibles. Au lieu d'instructions conditionnelles compliquées décrivant différents plans d'action, vous créez des objets interchangeables que vous sélectionnez en fonction de vos besoins. C'est l'objectif de base du polymorphisme.
L'interface commune fait partie intégrante du polymorphisme. Il existe deux manières de définir une interface en PHP: interfaces et classes abstraites. Les deux ont leurs utilisations, et vous pouvez les mélanger et les faire correspondre à votre guise, dans la hiérarchie des classes..
Une interface est similaire à une classe, sauf qu'elle ne peut pas contenir de code. Une interface peut définir des noms de méthodes et des arguments, mais pas le contenu des méthodes. Toute classe implémentant une interface doit implémenter toutes les méthodes définies par l'interface. Une classe peut implémenter plusieurs interfaces.
Une interface est déclarée en utilisant le 'interface
' mot-clé:
interface MyInterface // méthodes
et est attaché à une classe en utilisant le 'met en oeuvre
'mot-clé (plusieurs interfaces peuvent être implémentées en les listant séparées par des virgules):
La classe MyClass implémente MyInterface // méthodes
Les méthodes peuvent être définies dans l'interface comme dans une classe, sauf sans le corps (la partie entre les accolades):
interface MyInterface fonction publique doThis (); fonction publique doThat (); fonction publique setName ($ name);
Toutes les méthodes définies ici devront être incluses dans les classes d'implémentation exactement comme décrit. (lisez les commentaires du code ci-dessous)
// Classe VALID MyClass implémente MyInterface protected $ name; fonction publique doThis () // code qui fait cela fonction publique doThat () // code qui fait cela fonction publique setName ($ name) $ this-> name = $ name; // La classe INVALID MyClass implémente MyInterface // doThis () manquant! fonction privée doThat () // cela devrait être public! public function setName () // manque l'argument de nom!
Une classe abstraite est un mélange entre une interface et une classe. Il peut définir des fonctionnalités ainsi que des interfaces (sous forme de méthodes abstraites). Classes prolongeant une classe abstraite doit implémenter toutes les méthodes abstraites définies dans la classe abstraite.
Une classe abstraite est déclarée de la même manière qu'une classe avec l'ajout de 'abstrait
' mot-clé:
classe abstraite MyAbstract // méthodes
et est attaché à une classe en utilisant le 's'étend
' mot-clé:
class MyClass étend MyAbstract // méthodes de classe
Les méthodes régulières peuvent être définies dans une classe abstraite, tout comme dans une classe régulière, ainsi que n'importe quelle méthode abstraite (en utilisantabstrait
' mot-clé). Les méthodes abstraites se comportent exactement comme des méthodes définies dans une interface et doivent être implémentées exactement comme définies par les classes d'extension..
classe abstraite MyAbstract public $ name; fonction publique doThis () // do this fonction publique abstraite doThat (); fonction publique abstraite setName ($ name);
Imaginons que vous ayez un Article
classe responsable de la gestion des articles sur votre site Web. Il contient des informations sur un article, notamment le titre, l'auteur, la date et la catégorie. Voici à quoi ça ressemble:
classe poly_base_Article public $ title; public $ author; public $ date; catégorie public $; fonction publique __construct ($ title, $ author, $ date, $ category = 0) $ this-> title = $ title; $ this-> author = $ author; $ this-> date = $ date; $ this-> catégorie = $ catégorie;
Remarque: Les exemples de classes de ce didacticiel utilisent la convention de dénomination "package_component_Class". C'est un moyen courant de séparer les classes en espaces de noms virtuels pour éviter les conflits de noms..
Vous souhaitez maintenant ajouter une méthode pour exporter les informations dans différents formats, tels que XML et JSON. Vous pourriez être tenté de faire quelque chose comme ça:
class poly_base_Article //… fonction publique write ($ type) $ ret = "; switch ($ type) case 'XML': $ ret = ''; $ ret. = ' '; Pause; case 'JSON': $ array = array ('article' => $ obj); $ ret = json_encode ($ array); Pause; return $ ret;'. $ obj-> titre. ' '; $ ret. = ''. $ obj-> auteur. ' '; $ ret. = ''. $ obj-> date. ' '; $ ret. = ''. $ obj-> catégorie. ' '; $ ret. = '
C'est une sorte de solution laide, mais cela fonctionne - pour le moment. Demandez-vous ce qui se passera dans le futur quand nous voudrons ajouter plus de formats? Vous pouvez continuer à éditer la classe, en ajoutant de plus en plus de cas, mais maintenant vous ne faites que diluer votre classe..
Un principe important de la programmation orientée objet est qu’une classe devrait faire une chose et qu’elle devrait bien le faire..
Dans cet esprit, les instructions conditionnelles doivent être un drapeau rouge indiquant que votre classe essaie de faire trop de choses différentes. C'est là qu'intervient le polymorphisme.
Dans notre exemple, il est clair que deux tâches sont présentées: la gestion des articles et le formatage de leurs données. Dans ce tutoriel, nous allons reformuler notre code de formatage dans un nouvel ensemble de classes et découvrir à quel point il est facile d’utiliser le polymorphisme..
La première chose à faire est de définir l'interface. Il est important de bien réfléchir à votre interface, car toute modification de celle-ci peut nécessiter des modifications du code d'appel. Dans notre exemple, nous utiliserons une interface simple pour définir notre seule méthode:
interface poly_writer_Writer fonction publique write (poly_base_Article $ obj);
C'est si simple; nous avons défini un public écrire()
méthode qui accepte un objet Article comme argument. Toutes les classes implémentant l'interface Writer seront sûres d'avoir cette méthode.
Pointe: Si vous souhaitez limiter le type d'arguments pouvant être transmis à vos fonctions et méthodes, vous pouvez utiliser des indicateurs de type, comme nous l'avons fait dans écrire()
méthode; il n'accepte que les objets de type poly_base_Article
. Malheureusement, les suggestions de type de retour ne sont pas supportées dans les versions actuelles de PHP. Il vous appartient donc de prendre en charge les valeurs de retour..
Avec votre interface définie, il est temps de créer les classes qui font réellement des choses. Dans notre exemple, nous voulons sortir deux formats. Nous avons donc deux classes Writer: XMLWriter et JSONWriter. C’est à eux d’extraire les données de l’objet Article passé et de formater les informations..
Voici à quoi ressemble XMLWriter:
La classe poly_writer_XMLWriter implémente poly_writer_Writer fonction publique write (poly_base_Article $ obj) $ ret = ''; $ ret. = ' '; return $ ret;'. $ obj-> titre. ' '; $ ret. = ''. $ obj-> auteur. ' '; $ ret. = ''. $ obj-> date. ' '; $ ret. = ''. $ obj-> catégorie. ' '; $ ret. = '
Comme vous pouvez le voir dans la déclaration de classe, nous utilisons le met en oeuvre
mot-clé pour implémenter notre interface. le écrire()
méthode contient des fonctionnalités spécifiques au formatage XML.
Maintenant, voici notre classe JSONWriter:
La classe poly_writer_JSONWriter implémente poly_writer_Writer fonction publique write (poly_base_Article $ obj) $ array = array ('article' => $ obj); return json_encode ($ array);
Tout notre code spécifique à chaque format est maintenant contenu dans des classes individuelles. Ces classes ont chacune la responsabilité de gérer un format spécifique et rien d’autre. Grâce à notre interface, aucune autre partie de votre application ne doit se préoccuper de la façon dont cela fonctionne pour pouvoir l'utiliser..
Avec nos nouvelles classes définies, il est temps de revoir notre classe Article. Tout le code qui a vécu dans l'original écrire()
Cette méthode a été intégrée à notre nouvel ensemble de classes. Tout ce que notre méthode doit faire maintenant est d’utiliser les nouvelles classes, comme ceci:
class poly_base_Article //… fonction publique write (poly_writer_Writer $ writer) return $ writer-> write ($ this);
Tout ce que cette méthode fait maintenant, c'est accepter un objet de la classe Writer (c'est-à-dire toute classe implémentant l'interface Writer), appeler son écrire()
méthode, se passant elle-même ($ this
) comme argument, puis transmettez sa valeur de retour directement au code client. Il n'a plus besoin de s'inquiéter des détails du formatage des données et peut se concentrer sur sa tâche principale..
Vous vous demandez peut-être où vous obtenez un objet Writer pour commencer, car vous devez en passer un à cette méthode. Cela dépend de vous et il existe de nombreuses stratégies. Par exemple, vous pouvez utiliser une classe de fabrique pour récupérer les données de la demande et créer un objet:
class poly_base_Factory fonction statique publique getWriter () // variable de requête grab $ format = $ _REQUEST ['format']; // construit notre nom de classe et vérifie son existence $ class = 'poly_writer_'. $ format. 'Écrivain'; if (class_exists ($ class)) // renvoie un nouvel objet Writer renvoie new $ class (); // sinon nous échouons avec une nouvelle exception ('Format non supporté');
Comme je l'ai dit, il existe de nombreuses autres stratégies à utiliser en fonction de vos besoins. Dans cet exemple, une variable de demande choisit le format à utiliser. Il construit un nom de classe à partir de la variable de requête, vérifie son existence, puis renvoie un nouvel objet Writer. S'il n'en existe aucun sous ce nom, une exception est générée pour permettre au code client de déterminer quoi faire..
Avec tout en place, voici comment notre code client pourrait tout mettre ensemble:
$ article = new poly_base_Article ('Polymorphism', 'Steve', time (), 0); try $ writer = poly_base_Factory :: getWriter (); catch (Exception $ e) $ writer = new poly_writer_XMLWriter (); echo $ article-> write ($ writer);
Nous avons d’abord créé un exemple d’objet Article avec lequel travailler. Ensuite, nous essayons d’obtenir un objet Writer de la fabrique, en revenant à un défaut (XMLWriter) si une exception est levée. Enfin, nous passons l’objet Writer à notre article écrire()
méthode, impression du résultat.
Dans ce tutoriel, je vous ai fourni une introduction au polymorphisme et une explication des interfaces en PHP. J'espère que vous réalisez que je ne vous ai montré qu'un cas d'utilisation potentiel du polymorphisme. Il y a beaucoup, beaucoup plus d'applications. Le polymorphisme est un moyen élégant d'échapper aux déclarations conditionnelles laides de votre code POO. Il suit le principe de la séparation de vos composants et fait partie intégrante de nombreux modèles de conception. Si vous avez des questions, n'hésitez pas à demander dans les commentaires!