Un des principes de la programmation objet s'appelle l'encapsulation. Il permet de garder la cohérence des données stockées dans un objet en restreignant l'accés aux attributs et méthodes de cet objet.
L'encapsulation est réalisée en définissant des droits d'accès sur les données d'un objet. On donne ainsi plus ou moins de visibilité au monde extérieur sur l'intérieur de l'objet. On fait souvent l'analogie avec une boîte noire. Par exemple un moteur de voiture peut être considéré comme une boîte noire : on n'a pas à connaître son fonctionnement détaillé pour pouvoir utiliser la voiture dont il fait partie.
Les méthodes ne doivent pouvoir être atteintes directement que si elles concernent la "communication" avec le monde extérieure. Si elles concernent le fonctionnement interne de l'objet elles doivent être cachées.
Le concepteur d'une classe ne doit jamais laisser l'utilisateur d'une classe manipuler les objets instanciés sans restriction (même si le concepteur est également l'utilisateur). L'utilisateur d'une classe doit se contenter d'utiliser les méthodes mises à sa disposition, sans connaître les détails des traitements éventuellement réalisés sur les attributs.
En n'utilisant un objet qu'à travers ses méthodes, on a la garantie de conserver l'objet dans un état cohérent et prévu par le concepteur.
Le fait d'accéder aux attributs d'une classe uniquement par des méthodes offre également l'avantage de pouvoir modifier ou faire évoluer un objet sans changer son comportement extérieur (ie les méthodes à invoquer). Cela permet de séparer la spécification du comportement d'un objet de l'implémentation en langage informatique de cette spécification.
La visibilité d'un attribut ou d'une méthode définit l'endroit à partir duquel on peut avoir
accès à cet attribut ou à cette méthode.
Trois mots-clés permettent de définir cette visibilité.
| Endroit | Mot-clé | Remarque |
| Uniquement la classe | private |
La visibilité private est la plus faible (le droit d'accès le plus
restreint). Les éléments private ne sont vus que depuis l'intérieur de la classe
qui les implémente. On ne pourra pas les atteindre depuis un autre endroit du code (autres
classes, autres endroits du script). En UML La visibilité private est symbolisée par le signe moins (-).
|
| La classe et ses enfants | protected |
La visibilité protected est une extension de private. Les
éléments protected sont vus depuis la classe qui les implémente et depuis les
classes qui héritent de cette super-classe. Les autres classes et les autres endroits du
script n'auront pas accès à ses éléments.En UML La visibilité protected est symbolisée par le signe dièse (#).
|
| Partout | public |
La visibilité public est celle qui donne le plus de visibilité aux
élements. Ils sont visible et accessibles de tous les endroits du code. Ce type de visibilité
brise la règle de l'encapsulation des données.En UML La visibilité public est symbolisée par le signe plus (+).
|
Les mots-clés de visibilité se placent au début de la définition des attributs ou des méthodes.
Quand la visibilité d'un attribut ou d'une méthode est définie, cette visibilité ne peut plus être changée dans le code.
La visibilité protected sera étudiée quand nous parlerons de l'héritage.
L'exemple suivant reprend la classe Guitare et rend public les attributs nombre de
cordes et taux de remise. La méthode getPrix() est quant à elle passée en private.
Après instanciation d'un objet guitare, on peut donc changer son nombre de cordes de n'importe
où dans le script. Cette opération est bien évidemment impossible dans le monde réel où par
exemple un modèle 4 cordes ne peut pas être changé en 12 cordes
On peut également modifier la remise sans contrôle. Ici on donne une valeur négative ce qui a
pour effet non pas de faire une remise mais de doubler le prix de la guitare.
Finalement, le script s'arrête avec un message d'erreur quand on essaye depuis l'extérieur de la
classe d'invoquer la méthode private getPrix().
Pour mettre en place le principe de l'encapsulation (masquer les données), mais néanmoins permettre d'en voir et d'en modifier certaines avec ou sans condition de validité, on va définir des méthodes spécialisées dans ces traitements.
Une méthode spécialisée dans la lecture de la valeur d'un attribut s'appelle un accesseur
ou getter (prononcer guetteur). Par convention ces méthodes sont préfixées par
get suivi du nom de l'attribut dont elles renvoient la valeur.
Exemple : getModele(), getRemise(), ou get_modele(), get_remise(),
etc. suivant votre style de nommage.
Une méthode spécialisée dans l'écriture de la valeur d'un attribut s'appelle un mutateur
ou setter (prononcer setteur). Par convention ces méthodes sont préfixées par set
suivi du nom de l'attribut dont elles modifient la valeur..
Exemple : setModele(), setRemise(),ou set_modele(), set_remise(),
etc. suivant votre style de nommage.
Généralement les accesseurs et les mutateurs sont des méthodes public puisqu'elles
sont sensées permettre à l'extérieur d'accèder à des attributs intérieurs (private
ou protected).
Tous les attributs ne doivent pas forcément avoir des getters et/ou des setters. Seuls les attributs exposés au monde extérieur en ont besoin.
Reprenez la classe Livre de l'exercice précédent et écrivez les setters pour tous
les attributs.
Les attributs titre et auteur ne doivent pas être vides.
L'attribut pages doit être un nombre entre 10 et 1000.
L'attribut prix doit être un nombre entre 0 et 1000.
L'attribut cat doit être "Méthode", "Partition", "Biographie" ou "Autre".
Si les valeurs passées aux setters ne sont pas valides, l'attribut n'est pas modifié et un
message d'erreur doit être affiché et le script continue son éxécution sans s'arrêter.
Le message d'erreur affiché doit être de la forme 'Valeur [valeur passée] invalide pour
l'attribut [nom attribut].'
Pour tester, une instances de la classe Livre doit être créée de la façon suivante :
$livre = new Livre('Débutant Guitare Acoustique', 'Rébillard', 70, 17, 'Méthode');
Voud devez ensuite invoquer chacun des setters une valeur invalide puis une valeur valide pour vérifier que votre
traitement est correct.
Parfois, quand les attributs d'une classe sont nombreux, il peut être pénible d'écrire des getters et des setters dont la plupart n'auront qu'une ligne de code.
Pour nous faciliter la tâche, PHP met à notre disposition ce qu'on appelle des "méthodes
magiques". Deux d'entre elles vont dans notre cas être bien utiles : la méthode magique __get()
et la méthode magique __set().
Comme leur nom l'indique, __get() est un getter et __set() est un
setter.
__get() accepte en paramètre le nom de l'attribut pour lequel on veut connaître la
valeur.
__set() accepte 2 paramètres : le nom de l'attribut pour lequel on veut modifier la
valeur et la nouvelle valeur à affecter à l'attribut.
__get() et __set()sont invoqué automatiquement par PHP quand le monde
extérieur essaye d'accéder à un attribut dont l'accés lui est refusé.
Le refus d'accès peut avoir 2 causes :
- l'attribut est private ou protected,
- l'attribut n'est pas défini dans la classe.
Dans le code de ces 2 méthodes, nous définissons ce qui est fait dans le cas ou l'attribut n'est pas défini et ce qui est fait si l'attribut est défini : renvoi ou pas d'une valeur, modification ou pas d'une valeur
Les méthodes magiques dans PHP commencent toutes par un double underscore __ suivi
du nom de la méthode.
Tous les attributs ne doivent pas forcément avoir des getters et/ou des setters. Seuls les attributs exposés au monde extérieur en ont besoin.
La méthode magique __set() fonctionne comme __get() à la différence
qu'elle permet au monde extérieur de modifier la valeur d'un attribut. A mon avis l'utilisation
de __set() est à éviter car il faut toujours vérifier les valeurs des attributs
pour que l'état de l'objet reste cohérent. Comme on l'a vu précédemment, permet de modifier la
valeur de l'attribut remise sans contrôle permet d'introduire des erreurs dans le
traitement du prix de vente d'un livre.
L'exemple suivant montre l'utilisation de __set(). Encore une fois, je pense qu'il
faut mieux ne pas utiliser cette méthode dans les développements car on perd les contrôles que
l'on peut faire sur les valeurs des attributs, ou ses contrôles s'ils sont faits dans __set()
donneront souvent un code difficile à maintenir.