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.

Encapsulation

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.

Visibilité

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.

définir la visibilité des attributs et 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().

Exemple : visibilité des attributs et des méthodes

Accesseurs et mutateurs

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.

Exemple : la classe guitare avec accesseurs et mutateurs

Exercice : getter et setter

la classe Livre

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.

affichage obtenu

Une solution possible

Exercice : getters et setters

Getters et setters magiques

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.

Exemple : méthode magique __get()

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.

Exemple : méthode magique __set()