Le concept de couplage est fondamental en architecture logicielle. Un code fortement couplé est rigide : toute modification mineure peut entraîner une réaction en chaîne de révisions nécessaires. Pour illustrer ce problème et voir comment le pattern Simple Factory y répond, examinosn l'exemple d'un moteur de calcul.
L'approche monolithique : Le problème du couplage fort
Une erreur courante consiste à regrouper toute la logique de décision et de traitement au même endroit. Voici une implémentation typique qui manque de flexibilité :
// Exemple d'un code où la logique est entremêlée
public class CalculatriceNaive {
public static void main(String[] args) {
double v1 = 10;
double v2 = 5;
String operateur = "/";
double resultat = 0;
// Problème : Si nous voulons ajouter la racine carrée ou le modulo,
// nous devons modifier ce bloc de code existant.
switch (operateur) {
case "+":
resultat = v1 + v2;
break;
case "-":
resultat = v1 - v2;
break;
case "*":
resultat = v1 * v2;
break;
case "/":
if (v2 != 0) {
resultat = v1 / v2;
} else {
System.out.println("Erreur : Division par zéro.");
}
break;
}
System.out.println("Résultat : " + resultat);
}
}
Cette structure présente deux inconvénients majeurs. Premièrement, elle viole le principe de responsabilité unique. Deuxièmement, elle expose toute la logique métier lors de l'ajout d'une fonctionnalité, ce qui augmente le risque d'introduire des régressions.
Transition vers l'abstraction
Pour réduire le couplage, nous devons séparer chaque opération dans sa propre classe en utilisant l'héritage. Cela permet d'isoler les comportements.
public abstract class OperationMathematique {
protected double operandeA;
protected double operandeB;
public void setOperandes(double a, double b) {
this.operandeA = a;
this.operandeB = b;
}
public abstract double calculer() throws ArithmeticException;
}
class Somme extends OperationMathematique {
@Override
public double calculer() {
return operandeA + operandeB;
}
}
class Division extends OperationMathematique {
@Override
public double calculer() {
if (operandeB == 0) throw new ArithmeticException("Division par zéro impossible.");
return operandeA / operandeB;
}
}
Bien que cette structure soit plus propre, le client (l'application) ne sait toujours pas quelle instance créer dynamiquement. C'est ici qu'intervient le pattern Simple Factory.
Le rôle de la Simple Factory
Le Simple Factory (ou Fabrique Simple) est une classe spécialisée dont l'unique mission est de créer des objets. Elle centralise l'instanciation des sous-classes en fonction d'un paramètre donné.
public class FabriqueCalcul {
public static OperationMathematique creerOperation(String type) {
OperationMathematique op = null;
// La logique de création est centralisée ici
switch (type) {
case "addition":
op = new Somme();
break;
case "division":
op = new Division();
break;
// Facile d'ajouter de nouveaux types sans impacter le client
}
return op;
}
}
// Utilisation par le code client
public class ClientApp {
public static void main(String[] args) {
OperationMathematique monCalcul = FabriqueCalcul.creerOperation("addition");
monCalcul.setOperandes(15.5, 4.5);
System.out.println("Résultat final : " + monCalcul.calculer());
}
}
En adoptant cette architecture, l'extension du logiciel devient beaucoup plus simple. Pour ajouter une nouvelle opération (comme une multiplication), il suffit de créer une nouvelle classe héritant de OperationMathematique et de mettre à jour le switch dans la fbarique. Le reste de l'application demeure inchangé, garantissant ainsi une meilleure maintenaiblité et une plus grande robustesse.