Introduction aux modèles de conception créatifs

Introduction aux modèles de conception créatifs

1. introduction

En génie logiciel, un modèle de conception décrit une solution établie aux problèmes les plus couramment rencontrés dans la conception de logiciels. Il représente les meilleures pratiques développées sur une longue période grâce aux essais et aux erreurs de développeurs de logiciels expérimentés.

Design Patterns a gagné en popularité après la publication du livreDesign Patterns: Elements of Reusable Object-Oriented Software en 1994 par Erich Gamma, John Vlissides, Ralph Johnson et Richard Helm (également connu sous le nom de Gang of Four ou GoF).

Dans cet article, nous allons explorer les modèles de conception créative et leurs types. Nous allons également examiner quelques exemples de code et discuter des situations dans lesquelles ces modèles correspondent à notre conception.

2. Modèles de conception créative

Creational Design Patterns are concerned with the way in which objects are created. Ils réduisent les complexités et l'instabilité en créant des objets de manière contrôlée.

L'opérateurnew est souvent considéré comme dangereux car il disperse les objets dans toute l'application. Au fil du temps, il peut être difficile de modifier une implémentation car les classes sont étroitement associées.

Creational Design Patterns résout ce problème en découplant entièrement le client du processus d’initialisation proprement dit.

Dans cet article, nous aborderons quatre types de modèles de conception créative:

  1. Singleton - Garantit qu’une seule instance d’un objet existe dans l’application.

  2. Méthode Factory - Crée des objets de plusieurs classes associées sans spécifier l'objet exact à créer.

  3. Abstract Factory - Crée des familles d'objets dépendants liés

  4. Builder Construit des objets complexes en utilisant une approche étape par étape

Voyons maintenant chacun de ces modèles en détail.

3. Modèle de conception singleton

Le Singleton Design Pattern vise à garder un contrôle sur l'initialisation des objets d'une classe particulière parensuring that only one instance of the object exists throughout the Java Virtual Machine.

Une classe Singleton fournit également un seul point d'accès global à l'objet, de sorte que chaque appel ultérieur au point d'accès ne renvoie que cet objet particulier.

3.1. Exemple de modèle de singleton

Bien que le modèle Singleton ait été introduit par GoF, l'implémentation d'origine est connue pour être problématique dans les scénarios multithread.

Alors ici, nous allons suivre une approche plus optimale qui utilise une classe interne statique:

public class Singleton  {
    private Singleton() {}

    private static class SingletonHolder {
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

Ici, nous avons créé une classe internestatic qui contient l'instance de la classeSingleton. Il crée l'instance uniquement lorsque quelqu'un appelle la méthodegetInstance() et non lorsque la classe externe est chargée.

Cette approche est largement utilisée pour une classe Singleton car elle ne nécessite pas de synchronisation, est thread-safe, applique une initialisation lente et a comparativement moins de passe-partout.

Notez également que le constructeur a le modificateur d'accèsprivate. This is a requirement for creating a Singleton since a public constructor would mean anyone could access it and start creating new instances.

N'oubliez pas qu'il ne s'agit pas de la mise en œuvre originale du GoF. Pour la version originale, veuillez visiterthis linked example article sur Singletons en Java.

3.2. Quand utiliser le modèle de conception Singleton

  • Pour les ressources coûteuses à créer (comme les objets de connexion à la base de données)

  • Il est recommandé de conserver tous les enregistreurs sous forme de singletons, ce qui augmente les performances

  • Classes donnant accès aux paramètres de configuration de l'application

  • Classes contenant des ressources accessibles en mode partagé

4. Modèle de conception de méthode d'usine

Le modèle de conception Factory ou le modèle de conception Factory Method est l'un des modèles de conception les plus utilisés en Java.

Selon le GoF, ce modèle“defines an interface for creating an object, but let subclasses decide which class to instantiate. La méthode Factory permet à une classe de différer l'instanciation aux sous-classes ».

Ce modèle délègue la responsabilité d'initialiser une classe du client à une classe de fabrique particulière en créant un type de constructeur virtuel.

Pour ce faire, nous nous appuyons sur une usine qui nous fournit les objets en masquant les détails de la mise en œuvre. Les objets créés sont accessibles via une interface commune.

4.1. Exemple de modèle de conception de méthode d'usine

Dans cet exemple, nous allons créer une interfacePolygon qui sera implémentée par plusieurs classes concrètes. UnPolygonFactory sera utilisé pour récupérer les objets de cette famille:

image

Commençons par créer l’interfacePolygon:

public interface Polygon {
    String getType();
}

Ensuite, nous allons créer quelques implémentations telles queSquare,Triangle,, etc. qui implémentent cette interface et retournent un objet de typePolygon.

Nous pouvons maintenant créer une fabrique qui prend comme argument le nombre de côtés et renvoie l'implémentation appropriée de cette interface:

public class PolygonFactory {
    public Polygon getPolygon(int numberOfSides) {
        if(numberOfSides == 3) {
            return new Triangle();
        }
        if(numberOfSides == 4) {
            return new Square();
        }
        if(numberOfSides == 5) {
            return new Pentagon();
        }
        if(numberOfSides == 7) {
            return new Heptagon();
        }
        else if(numberOfSides == 8) {
            return new Octagon();
        }
        return null;
    }
}

Remarquez comment le client peut s'appuyer sur cette fabrique pour nous donner unPolygon approprié, sans avoir à initialiser l'objet directement.

4.2. Quand utiliser le modèle de conception de méthode d'usine

  • Lorsque l’implémentation d’une interface ou d’une classe abstraite doit changer fréquemment

  • Lorsque la mise en œuvre actuelle ne peut pas accueillir facilement de nouveaux changements

  • Lorsque le processus d'initialisation est relativement simple et que le constructeur n'a besoin que de quelques paramètres

5. Modèle de conception d'usine abstraite

Dans la section précédente, nous avons vu comment le modèle de conception Méthode d'usine pouvait être utilisé pour créer des objets associés à une seule famille.

En revanche, le motif de conception Abstract Factory est utilisé pour créer des familles d’objets apparentés ou dépendants. On l’appelle aussi parfois une usine d’usines.

Pour une explication détaillée, consultez notre tutorielAbstract Factory.

6. Modèle de conception de constructeur

Le modèle de conception du constructeur est un autre modèle de création conçu pour traiter la construction d'objets relativement complexes.

Lorsque la complexité de la création d'un objet augmente, le modèle Builder peut séparer le processus d'instanciation en utilisant un autre objet (un générateur) pour construire l'objet.

Ce générateur peut ensuite être utilisé pour créer de nombreuses autres représentations similaires à l’aide d’une approche simple, étape par étape.

6.1. Exemple de modèle de générateur

Le modèle de conception d'origine créé par GoF est axé sur l'abstraction et convient très bien aux objets complexes. Toutefois, sa conception est un peu compliquée.

Joshua Bloch, dans son livre Effective Java, a présenté une version améliorée du modèle de générateur qui est propre, hautement lisible (car il utilisefluent design) et facile à utiliser du point de vue du client. Dans cet exemple, nous allons discuter de cette version.

Cet exemple n'a qu'une seule classe,BankAccount qui contient un générateur en tant que classe interne destatic:

public class BankAccount {

    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;

    // constructors/getters

    public static class BankAccountBuilder {
        // builder code
    }
}

Notez que tous les modificateurs d'accès sur les champs sont déclarésprivate car nous ne voulons pas que les objets externes y accèdent directement.

Le constructeur est égalementprivate afin que seul le constructeur affecté à cette classe puisse y accéder. Toutes les propriétés définies dans le constructeur sont extraites de l'objet générateur que nous fournissons en tant qu'argument.

Nous avons définiBankAccountBuilder dans une classe interne destatic:

public static class BankAccountBuilder {

    private String name;
    private String accountNumber;
    private String email;
    private boolean newsletter;

    public BankAccountBuilder(String name, String accountNumber) {
        this.name = name;
        this.accountNumber = accountNumber;
    }

    public BankAccountBuilder withEmail(String email) {
        this.email = email;
        return this;
    }

    public BankAccountBuilder wantNewsletter(boolean newsletter) {
        this.newsletter = newsletter;
        return this;
    }

    public BankAccount build() {
        return new BankAccount(this);
    }
}

Notez que nous avons déclaré le même ensemble de champs que la classe externe. Tous les champs obligatoires sont requis comme arguments pour le constructeur de la classe interne, tandis que les champs facultatifs restants peuvent être spécifiés à l'aide des méthodes setter.

Cette implémentation prend également en charge l'approche de conception fluide en demandant aux méthodes de définition de renvoyer l'objet générateur.

Enfin, la méthode de génération appelle le constructeur privé de la classe externe et se passe comme argument. LesBankAccount renvoyés seront instanciés avec les paramètres définis par lesBankAccountBuilder.

Voyons un exemple rapide du modèle de générateur en action:

BankAccount newAccount = new BankAccount
  .BankAccountBuilder("Jon", "22738022275")
  .withEmail("[email protected]")
  .wantNewsletter(true)
  .build();

6.2. Quand utiliser le modèle de générateur

  1. Lorsque le processus de création d'un objet est extrêmement complexe, avec de nombreux paramètres obligatoires et facultatifs

  2. Quand une augmentation du nombre de paramètres de constructeur conduit à une longue liste de constructeurs

  3. Lorsque le client attend des représentations différentes pour l'objet qui est construit

7. Conclusion

Dans cet article, nous avons découvert les modèles de conception de création en Java. Nous avons également abordé leurs quatre types différents, à savoir Singleton, Méthode Factory, Abstract Factory et Builder Pattern, leurs avantages, des exemples et quand devrions-nous les utiliser.

Comme toujours, les extraits de code complets sontavailable over on GitHub.