Blog ACENSI
Picto ordinateur et code {Le pattern Abstract Factory}

Le pattern Abstract Factory

L’Abstract Factory (Fabrique Abstraite) va un peu plus loin que la Factory Method. En effet, contrairement au précédent pattern qui déléguait l’instanciation à une méthode, il se distingue par la création d’un objet « Factory ». Ainsi, ce pattern ne s’occupe QUE de l’instanciation de classe, représentée par des familles d’objets comme dans le Document ci-dessous.

Use case

Après plusieurs jours de développement (très) intensif, notre chef revient vers nous :
« Finalement, il n’y aura que les fichiers CSV et les fichiers Excel. Bon, par contre, nous devrions avoir des fichiers logs en cas de plantage de l’application… ».
Dans le Pattern Factory Method, nous avons déjà modélisé notre gestion des fichiers CSV et Excel.
Un fichier Logs est différent d’un fichier texte ou CSV. Jusque-là, nous ne devions qu’ouvrir des documents, les valider, puis les envoyer. Maintenant, nous devons créer un fichier, écrire à l’intérieur et gérer son niveau d’erreur.
C’est pourquoi nous allons créer une nouvelle famille d’objets de type Log. Ces logs peuvent tout à fait se faire dans une BD ou encore dans un fichier.

Représentation UML

UML de class Abstract Factory
Dans cette modélisation UML, nous pouvons distinguer deux fabriques : CSVFactory et ExcelFactory.
Ces dernières vont s’occuper de :

  • Retourner un fichier Document de leur type ;
  • Créer un fichier de Log selon leur type.

Afin de pouvoir utiliser ces deux fabriques de manière totalement abstraite, nous allons utiliser une interface : AbstractFactory. Cette dernière fera le lien entre le client, en renvoyant les « Objets Interface » (nos produits abstraits), et nos fabriques concrètes, en appelant la méthode souhaitée.

Représentation JAVA

Comme cet exercice reprend des éléments de notre Use Case sur la Factory Method, nous considérons que la gestion des fichiers CSV et Excel est déjà implémentée. Il en est de même pour la gestion des Logs (sensiblement le même principe). Nous allons maintenant nous intéresser à la création de notre Factory :

public interface AbstractFactory {
    public Document makeDocument(String name);
    public Log makeLog(String name);
}

Ci-dessus, notre interface qui sera exposée au client.
Elle ne contient que nos deux méthodes :

  • makeDocument() qui renverra un Document, en appelant la méthode makeDocument() de nos fabriques concrètes.
  • makeLog() qui renverra un objet de type Log, en appelant la méthode createLog() de nos fabriques concrètes.
public class CSVFactory implements AbstractFactory {
 
    private Document getFile(String name) {
        return new CSVFile(name);
    }
    private Log getLog(String name) {
        return new CSVLogFile(name);
    }
 
    public Document makeDocument(String name) {
        return getFile(name);
    }
 
    public Log makeLog(String name) {
        return getLog(name);
    }
}
public class ExcelFactory implements AbstractFactory {
 
    private Document getFile(String name) {
        return new ExcelFile(name);
    }
    private Log getLog(String name) {
        return new ExcelLogFile(name);
    }
 
    public Document makeDocument(String name) {
        return getFile(name);
    }
 
    public Log makeLog(String name) {
        return getLog(name);
    }
}

Afin d’utiliser de concert nos deux fabriques, sans devoir instancier nous-même la bonne classe, nous allons utiliser un « builder simplifié » :

public class FactoBuilder {
    public static AbstractFactory excelFactory() {
        return new ExcelFactory();
    }
    public static AbstractFactory csvFactory() {
        return new CSVFactory();
    }
    /*Private constructor only use these methods above*/
    private FactoBuilder () {};
}

Ce builder va permettre au client de récupérer le bon type de Factory, en fonction du fichier requis. Ainsi, la méthode excelFactory renverra un objet ExcelFactory, qui sera représenté par son interface AbstractFactory.
Nos deux méthodes sont alors statiques, ce qui permettra de récupérer une AbstractFactory, en fonction de la demande (Excel ou CSV). Mettre le constructeur en privé évite d’instancier inutilement une classe qui ne renvoie QUE des nouveaux objets (dans notre cas).
Grâce à cela, le développeur n’a plus du tout à se soucier de l’instanciation, désormais totalement camouflée par notre Abstract Factory.  \o/

public class Main {
    public static void main(String[] args) {
        AbstractFactory csv = FactoBuilder.csvFactory();
        Document csvDoc = csv.makeDocument("OttO.csv");
        Log csvLog = csv.makeLog("Log_OttO_yyyymmdd");
        csvLog.open();
        csvDoc.validate();
        csvLog.putLine("Validate");
        csvDoc.readLine();
        csvDoc.readLine();
        csvLog.putLines(csvDoc.readLine());
        csvDoc.close();
        csvLog.close();
    }
}

Output :

Opening csv Log_OttO_yyyymmdd
CSVFile OttO.csv validate : true
putting line [Validate]into csv Log file 
putting all lines into csv Log file
CSVFile OttO.csv close !
Closing csv Log_OttO_yyyymmdd

Conclusion

Le pattern Abstract Factory va plus loin dans la séparation du code et de l’instanciation. Ainsi :

  • Il fournit une interface permettant la création de familles d’objets, sans spécifier leur classe concrète (cf. CSVFile n’a jamais été communiqué au développeur).
  • Contrairement à la Factory Method, la Fabrique Abstraite DOIT connaître ses fabriques objets qu’elle pourrait ainsi instancier.
  • Il utilise la composition pour créer les objets.
  • Une Abstract Factory contient au moins une Factory Method.
  • Les méthodes dans la Fabrique Abstraite sont « Product-type-dependent », c’est-à-dire que si nous voulons ajouter un nouveau produit, il faudra modifier notre Interface.

La suite dans un prochain article.

Pourquoi ce blog ?

Pour permettre à nos consultants et experts techniques de partager leurs connaissances et retours d’expérience autour des sujets qui les passionnent. Ce blog, intégralement écrit par eux, a pour vocation d’être un véritable lieu d’échanges et d’apprentissage.

Alors n’hésitez pas à commenter nos articles pour rejoindre la conversation !

Une suggestion ?

Si vous avez des idées pour améliorer ce blog, nous sommes à l’écoute de vos remarques. Vous pouvez nous écrire via le formulaire de contact qui se trouve en bas de page.

Bonne visite !