1. Notion d’abstraction
En bref : l’abstraction, c’est l’art de voir les choses complexes de façon simple.
En programmation, l’abstraction est le processus qui consiste à dégager les caractéristiques et comportements communs d’un groupe d’objets, tout en ignorant les détails spécifiques (par exemple, la manière exacte dont quelque chose fonctionne « sous le capot »). Imaginez que vous dessinez une carte de la ville : on y indique les routes, les bâtiments, les rivières — mais pas des détails comme la couleur des rideaux à chaque fenêtre. La carte est une abstraction de la ville.
En POO, l’abstraction consiste à créer des classes et des interfaces qui ne reflètent que les propriétés et actions importantes pour la tâche, en masquant les détails superflus.
Exemples de la vie courante
- Le transport est une abstraction. Peu importe qu’il s’agisse d’un bus, d’un vélo ou d’un vaisseau spatial — l’essentiel est que tout moyen de transport a des points communs : il peut se déplacer, il a des passagers et un conducteur.
- L’animal est aussi une abstraction. Tous les animaux peuvent respirer, manger, se déplacer — mais la manière précise dépend de l’espèce.
- Le paiement est une abstraction dans les logiciels bancaires. Vous ne vous souciez pas toujours de savoir s’il s’agit d’un paiement par carte, via PayPal ou en bitcoins — l’essentiel est qu’il puisse être exécuté et produire un résultat.
Pourquoi l’abstraction est-elle importante ?
- Penser moins aux détails qui ne sont pas importants pour résoudre la tâche actuelle.
- Concevoir des systèmes plus faciles à étendre et à maintenir.
- Travailler avec des objets via une interface commune, sans se soucier de l’implémentation concrète.
2. Abstraction en Java
En Java, l’abstraction s’appuie principalement sur deux outils :
- Les classes abstraites (abstract class)
- Les interfaces (interface)
Dans cette leçon, nous nous concentrerons sur les classes abstraites. (Nous aborderons très bientôt les interfaces !)
Une classe abstraite est une classe qui n’est pas destinée à être instanciée directement. Elle fournit une base commune (un modèle) pour d’autres classes. Dans une classe abstraite, on peut définir à la fois des méthodes implémentées (avec un corps) et des méthodes abstraites (sans corps) — celles qui doivent impérativement être implémentées dans les sous-classes.
Une méthode abstraite est une méthode sans implémentation, c’est‑à‑dire sans corps. Elle indique : « Tous les descendants de cette classe doivent implémenter cette méthode à leur manière ».
Exemple : abstraction « Figure »
public abstract class Shape {
public abstract void draw(); // Méthode abstraite — pas de corps !
}
Ici, nous disons : « Toutes les figures savent se dessiner, mais je ne sais pas comment — que chaque sous‑classe décide par elle‑même. »
Pourquoi n’est‑il pas toujours nécessaire de connaître les détails d’implémentation?
Quand vous utilisez une abstraction, vous travaillez avec l’objet via sa « face » — l’ensemble des méthodes qu’il doit proposer. La manière précise dont cette méthode fonctionne n’a pas d’importance.
Par exemple, vous appelez payment.process() sur un objet pour effectuer un paiement. Peu importe comment c’est implémenté — tant que ça fonctionne. Cela permet de :
- Remplacer une implémentation par une autre sans réécrire le code qui l’utilise.
- Simplifier les tests (on peut substituer des implémentations par des « bouchons » (stubs)).
- Rendre le code plus flexible et plus résilient aux changements.
3. Avantages de l’abstraction
Simplifier la conception et la maintenance du code
L’abstraction permet d’ignorer le superflu. Vous n’avez pas besoin de savoir précisément comment fonctionne le moteur d’une voiture pour la conduire — il suffit d’un volant, de pédales et de l’instruction « avancer ». De même dans le code : si vous travaillez avec une classe abstraite ou une interface, vous ne voyez que ce dont vous avez besoin.
Facilité d’extension du système
Lorsque votre système est construit sur des abstractions, vous ajoutez facilement de nouveaux types d’objets. Par exemple, si vous avez une classe abstraite Shape, vous pouvez ajouter un nouveau type de figure — Triangle — sans modifier l’ancien code.
Réduction du couplage entre composants
Si différentes parties du programme communiquent uniquement via des abstractions, on peut les modifier indépendamment les unes des autres. C’est comme une prise et une fiche : si la norme est la même, vous pouvez brancher n’importe quel appareil.
4. Exemples pratiques
Voyons à quoi ressemble l’abstraction en pratique. Nous utiliserons des exemples que vous pouvez intégrer à votre application d’apprentissage afin de les manipuler vous‑même.
Exemple 1 : classe « Shape » (figure)
public abstract class Shape {
public abstract void draw();
}
Créons maintenant quelques figures concrètes :
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("On dessine un cercle");
}
}
public class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("On dessine un rectangle");
}
}
Utilisons l’abstraction :
Shape s1 = new Circle();
Shape s2 = new Rectangle();
s1.draw(); // On dessine un cercle
s2.draw(); // On dessine un rectangle
Ici, nous manipulons des variables de type Shape — et peu importe la figure réelle sous‑jacente. C’est toute la force de l’abstraction !
Exemple 2 : classe « Payment »
public abstract class Payment {
public abstract void process();
}
Implémentations concrètes :
public class CreditCardPayment extends Payment {
@Override
public void process() {
System.out.println("Traitement du paiement par carte de crédit");
}
}
public class PaypalPayment extends Payment {
@Override
public void process() {
System.out.println("Traitement du paiement via PayPal");
}
}
Utilisation :
Payment[] payments = {
new CreditCardPayment(),
new PaypalPayment()
};
for (Payment p : payments) {
p.process();
}
Au final, chaque paiement est traité à sa manière, mais le code qui les invoque ne s’en préoccupe pas.
5. Abstraction et détails d’implémentation: comment ne pas s’y perdre
L’abstraction, c’est le « quoi », pas le « comment »
Quand vous concevez une abstraction, vous répondez à la question : « Que doit savoir faire l’objet ? »
Les détails d’implémentation relèvent du « Comment le fait‑il ? ».
Par exemple :
- Abstraction : « Toute figure doit savoir se dessiner (draw()). »
- Détails d’implémentation : « Le cercle se dessine à l’aide d’une circonférence, le rectangle — avec quatre lignes. »
Analogie de la vie courante
Pensez à la télécommande. Vous ne vous souciez pas de la façon dont elle transmet le signal — l’important est qu’il y ait des boutons « allumer », « changer de chaîne » et « augmenter le volume ». C’est cela, l’abstraction — un ensemble de boutons sur lesquels vous pouvez appuyer. Les ingénieurs qui conçoivent la télécommande, eux, doivent penser aux détails d’implémentation.
6. Comment dégager des abstractions dans un programme
Étape 1. Trouver les points communs
Regardez les objets de votre domaine métier. Qu’ont‑ils en commun ? Par exemple, tous les véhicules peuvent se déplacer, tous les animaux peuvent manger, tous les paiements peuvent être effectués.
Étape 2. Définir une classe abstraite
Créez une classe abstraite qui ne contiendra que ce qui est commun à tous les objets.
public abstract class Transport {
public abstract void move();
}
Étape 3. Implémenter les détails dans les sous-classes
public class Car extends Transport {
@Override
public void move() {
System.out.println("La voiture roule sur la route");
}
}
public class Bicycle extends Transport {
@Override
public void move() {
System.out.println("Le vélo avance en pédalant");
}
}
Étape 4. Utiliser l’abstraction dans le code
Transport[] transports = {
new Car(),
new Bicycle()
};
for (Transport t : transports) {
t.move();
}
Votre code travaille avec des abstractions — et ne dépend pas des détails d’implémentation.
7. Abstraction vs détails: l’équilibre
Erreur typique des débutants — tenter de rendre l’abstraction trop détaillée ou, au contraire, trop générale.
- Si l’abstraction est trop générale (par exemple, une classe « Object » avec une méthode « doSomething »), elle n’apporte aucun bénéfice.
- Si l’abstraction est trop détaillée (par exemple, « Un cercle avec un contour rouge et un rayon de 5 »), elle perd son sens — il vaut mieux écrire directement une classe concrète.
Règle d’or : l’abstraction doit refléter uniquement ce qui est réellement commun et important pour votre tâche.
8. Erreurs courantes lors de l’utilisation de l’abstraction
Erreur n° 1: tenter d’instancier une classe abstraite.
Les classes abstraites ne sont pas destinées à être instanciées directement. Si vous essayez d’écrire Shape s = new Shape();, le compilateur affichera l’erreur : Cannot instantiate the type Shape. C’est comme essayer d’acheter simplement du « transport » dans un magasin. Impossible : il faut choisir un vélo, une voiture ou un bus précis.
Erreur n° 2: oublier d’implémenter une méthode abstraite dans la sous-classe.
Si une classe hérite d’une classe abstraite mais n’implémente pas toutes ses méthodes abstraites, elle devient elle aussi abstraite — et on ne peut pas l’instancier. Vérifiez que vous avez implémenté toutes les méthodes obligatoires.
Erreur n° 3: mélanger abstraction et détails d’implémentation.
Si vous commencez à ajouter dans la classe abstraite des détails qui ne servent qu’à une seule sous‑classe, c’est le signe d’une mauvaise abstraction. Par exemple, si la classe abstraite Payment contient un champ cardNumber alors que tous les paiements ne passent pas par carte.
Erreur n° 4: abuser des abstractions.
Il ne faut pas faire de l’abstraction pour l’abstraction. S’il n’existe qu’un seul type d’objet dans votre système et qu’il ne sera jamais étendu, l’abstraction ne fera que compliquer le code.
GO TO FULL VERSION