1. Intro
Imagine que tu conçois une voiture moderne. À l'intérieur — des centaines de pièces complexes, de l'électronique sensible, des réglages fins. Évidemment, personne ne devrait pouvoir accéder au bloc de gestion moteur et commencer à bidouiller des vis au hasard. Si tout le monde met les mains là où il ne faut pas, la bagnole peut devenir imprévisible, et tu vas devoir faire un contrôle technique pour comprendre qui a cassé quoi.
C'est pareil en prog. Quand tu crées une classe, tu veux protéger ses entrailles des mains baladeuses — pour que personne ne flingue des données importantes ou ne touche à des méthodes que le monde extérieur n'a pas à connaître. C'est ça, le principe de l'encapsulation — un des trois piliers de la programmation orientée objet.
L'encapsulation permet de cacher les détails d'implémentation et de bien définir ce qui est accessible "dehors" et ce qui doit rester à l'intérieur. C'est comme si tu laissais l'utilisateur ouvrir la boîte à gants, mais que tu verrouillais bien le capot. La classe décide elle-même quelles données montrer au code externe, et lesquelles garder secrètes. Grâce à ça, le code est plus fiable, plus logique et plus simple à maintenir.
Pour être plus précis, encapsulation — c'est une façon de regrouper les données (champs) et le comportement (méthodes) qui va avec, dans une seule structure, tout en limitant l'accès direct aux composants internes de l'objet.
2. Modificateurs d'accès : on protège son territoire
En C# (et dans d'autres langages OOP), encapsulation se fait grâce aux modificateurs d'accès. Ce sont des "étiquettes" qui disent quels membres de la classe (champs, méthodes, propriétés, etc.) sont accessibles de l'extérieur, et lesquels — seulement de l'intérieur.
On les a déjà croisés, mais petit rappel et on élargit un peu la liste :
| Modificateur | Accessible... |
|---|---|
|
À tout le monde ! N'importe quel code du projet (et même en dehors si c'est une lib) |
|
Seulement à l'intérieur de cette classe |
|
Dans cette classe et tous ses enfants |
|
Seulement dans l'assembly (le projet) courant |
|
Soit dans l'assembly courant, soit dans un héritier |
|
Seulement dans un héritier à l'intérieur de l'assembly courant |
Dans cette conf, on va se concentrer sur les plus utilisés : public, private et on va juste toucher un mot sur protected, histoire de pas te surcharger le cerveau.
On sépare l'interne de l'externe
Écrivons une classe Dog :
public class Dog
{
public string Name;
public int Age;
public void Bark()
{
Console.WriteLine($"{Name} dit : Ouaf !");
}
}
Ici, tous les champs et méthodes sont déclarés avec le modificateur public. Ça veut dire que n'importe qui peut changer le nom ou l'âge du chien :
Dog rex = new Dog("Rex", 5);
rex.Name = "Medor"; // Changement d'identité inattendu !
rex.Age = -999; // Gros souci d'âge
Mais les champs Name et Age — c'est des infos super importantes, qu'on n'a pas envie de laisser à n'importe qui.
À ne pas faire comme ça
Si tu laisses tous les champs public, tu risques de casser la logique de la classe : n'importe quelle intervention externe peut rendre l'objet invalide. Genre, donner un âge négatif au chien ou lui mettre un nom du style "%$#!??".
3. On cache les champs : on utilise private
En général, les champs d'une classe sont privés (avec le modificateur private). Ça veut dire qu'on ne peut changer leur valeur que depuis l'intérieur de la classe, et pas de l'extérieur.
public class Dog
{
private string name;
private int age;
public void Bark()
{
Console.WriteLine($"{name} dit : Ouaf !");
}
}
Maintenant, si on essaie d'accéder aux champs directement depuis l'extérieur :
Dog rex = new Dog("Rex", 5);
rex.name = "Medor"; // Erreur de compilation
Le compilateur va direct te dire : « Pas d'accès ! ».
Pourquoi être aussi strict ? Où est la flexibilité ?
Toute la "flexibilité" vient de méthodes spéciales ou de propriétés (on en parlera plus dans la prochaine conf), qui permettent de contrôler l'accès et de modifier les données seulement selon certaines règles.
4. Encapsulation en pratique : exemple avec contrôle
Imaginons qu'on veuille empêcher qu'un chien ait un âge négatif :
public class Dog
{
private string name;
private int age;
public Dog(string name, int age)
{
this.name = name;
if (age >= 0)
this.age = age;
else
this.age = 0; // On n'autorise pas un "âge chelou"
}
public void Bark()
{
Console.WriteLine($"{name} dit : Ouaf !");
}
// Méthode pour changer l'âge en toute sécurité
public void SetAge(int newAge)
{
if (newAge >= 0)
age = newAge;
// On peut ajouter else : message d'erreur sur la valeur incorrecte
}
}
Maintenant, personne ne peut casser les champs directement de l'extérieur. Pour changer l'âge, il y a une méthode spéciale qui fait la vérif.
5. Champs vs méthodes d'accès (getters/setters)
Cette façon de faire — mettre les champs en private et fournir des méthodes pour bosser avec — on appelle ça encapsulation des données (data encapsulation). Pour lire la valeur d'un champ, on crée souvent des méthodes getter, et pour écrire — des setters.
public class Dog
{
private string name;
public Dog(string name)
{
this.name = name;
}
public string GetName()
{
return name;
}
public void SetName(string newName)
{
// Ici tu peux ajouter une vérif sur le nom
name = newName;
}
}
Mais ! Avec l'arrivée des propriétés (properties) en C#, on utilise de moins en moins ce genre de méthodes — les propriétés rendent le code bien plus clean et pratique (on en parle plus dans la prochaine conf).
6. Modificateurs d'accès pour méthodes et classes
Les champs, c'est pas tout ! Les modificateurs d'accès servent aussi pour les méthodes (fonctions membres de la classe) et même pour les classes elles-mêmes.
Les méthodes utilisées seulement à l'intérieur de la classe (genre des fonctions utilitaires pour la logique interne), on les met en private.
Les méthodes publiques — c'est ce qu'on appelle l'interface de la classe (à ne pas confondre avec le mot-clé interface), c'est-à-dire ce que l'utilisateur de la classe peut utiliser.
7. Erreurs classiques avec l'encapsulation
Erreur n°1 : tout est déclaré public.
On croit que plus il y a d'accès, plus c'est simple à utiliser. En vrai — ça ouvre l'accès à des parties internes de la classe qui ne devraient pas être modifiables de l'extérieur. Ce genre de code devient vulnérable et imprévisible, surtout dans les gros projets.
Erreur n°2 : changer le modificateur d'accès casse le code externe.
En refactorant, tu peux changer par erreur le modificateur d'accès d'une méthode ou d'un champ, et tout le reste du code qui bossait avec va d'un coup ne plus compiler ou ne plus marcher comme il faut. C'est super critique surtout dans les API publiques.
Erreur n°3 : confusion entre variables locales et champs de classe.
Parfois, les devs oublient qu'une variable déclarée dans une méthode ne vit que dans cette méthode. Les champs de classe, eux, sont accessibles dans toutes les méthodes de la classe. Ça donne des bugs pas évidents, surtout si les noms de variables sont les mêmes.
Erreur n°4 : négliger private et protected.
Beaucoup ont peur d'utiliser l'accès limité, de peur de ne plus pouvoir accéder à un élément plus tard. Mais l'encapsulation, c'est justement ça — cacher tout ce qui est inutile, et n'exposer que ce qui est vraiment nécessaire.
GO TO FULL VERSION