Introduction : « Pourquoi ne pas simplement rendre les champs publics ? »
\n\nImaginez la situation : vous écrivez une classe Person en Java. Elle a un nom, un âge et un email. La façon la plus simple est de déclarer les champs public et d'y accéder directement :
public class Person {\n public String name;\n public int age;\n public String email;\n}\n\n« Ça marche ! — direz-vous. — Pourquoi aurais-je besoin de vos getters et setters ? Du code en plus, les lignes s'allongent… »
\n\nCette question est l'une des plus populaires sur Stack Overflow. Des milliers de développeurs, surtout débutants, se la posent. La réponse que vous obtiendrez d'un programmeur Java expérimenté sera courte : l'encapsulation. Mais derrière ce mot se cache toute une philosophie de conception qui sauve les projets du chaos.
\n\nDans cet article, nous ne nous contenterons pas de donner une définition. Nous analyserons les problèmes réels que résolvent les getters et setters (également appelés accesseurs et mutateurs), et nous le montrerons avec du code concret. C'est parti !
\n\n1. L'encapsulation : pas qu'un mot à la mode
\n\nL'encapsulation est un principe de la POO qui stipule : cache la structure interne de l'objet et fournis une interface contrôlée pour interagir avec lui. Les getters et setters sont précisément cette interface contrôlée.
\n\nComparez les deux approches :
\n\nMauvaise approche (champs publics)
\nPerson person = new Person();\nperson.age = -5; // Âge négatif ? Facile !\nperson.email = "pas-un-email"; // Des données inutiles passent sans vérification\n\nBonne approche (getters et setters)
\npublic class Person {\n private String name;\n private int age;\n private String email;\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n if (age < 0 || age > 150) {\n throw new IllegalArgumentException("L'âge doit être compris entre 0 et 150");\n }\n this.age = age;\n }\n\n public String getEmail() {\n return email;\n }\n\n public void setEmail(String email) {\n if (email == null || !email.contains("@")) {\n throw new IllegalArgumentException("Email invalide");\n }\n this.email = email;\n }\n\n // autres getters/setters...\n}\n\nMaintenant, si quelqu'un essaie de définir un âge de -5, le programme lèvera une exception. Les données sont protégées. Voilà la puissance de l'encapsulation.
\n\n2. Validation des données à la volée
\n\nLes setters sont l'endroit idéal pour vérifier les données d'entrée. Sans eux, vous devriez écrire la validation à chaque endroit où vous assignez une valeur. Cela entraîne une duplication de code et un risque d'oublier une vérification quelque part.
\n\nExemple avec un compte bancaire :
\n\npublic class BankAccount {\n private double balance;\n\n public double getBalance() {\n return balance;\n }\n\n public void deposit(double amount) {\n if (amount <= 0) {\n throw new IllegalArgumentException("Le montant du dépôt doit être positif");\n }\n this.balance += amount;\n }\n\n public void withdraw(double amount) {\n if (amount <= 0) {\n throw new IllegalArgumentException("Le montant du retrait doit être positif");\n }\n if (amount > balance) {\n throw new IllegalStateException("Solde insuffisant");\n }\n this.balance -= amount;\n }\n}\n\nRemarquez : il n'y a pas de setter direct pour balance. Nous gérons le solde via les méthodes deposit et withdraw, qui contiennent déjà toute la logique de vérification. Le getter, quant à lui, renvoie simplement l'état actuel.
3. Flexibilité : vous pouvez modifier l'implémentation sans casser le code
\n\nImaginez que vous stockez l'âge dans un champ int, puis que vous décidez de stocker la date de naissance et de calculer l'âge dynamiquement. Si vous aviez des
Blogs
Book Recommendations
Lire aussi
Concurrence en C++ : des threads aux coroutines et async/await
String vs string en TypeScript : lequel choisir pour la performance ?
Comment mesurer les performances en Rust : benchmarking pour débutants
Generics en C++ : pourquoi en a-t-on besoin et comment les utiliser en pratique
Travailler avec WebAssembly et C++ : de A à Z