JavaRush /Blog Java /Random-FR /Modèles de conception : FactoryMethod

Modèles de conception : FactoryMethod

Publié dans le groupe Random-FR
Bonjour! Aujourd'hui, nous continuerons à étudier les modèles de conception et à parler de la méthode d'usine (FactoryMethod). Modèles de conception : FactoryMethod - 1Vous découvrirez de quoi il s’agit et à quelles tâches ce modèle est adapté. Nous examinerons ce modèle de conception en pratique et explorerons sa structure. Pour que tout ce qui précède soit clair pour vous, vous devez comprendre les sujets suivants :
  1. Héritage en Java.
  2. Méthodes et classes abstraites en Java.

Quel problème la méthode d’usine résout-elle ?

Dans tous les modèles de conception d'usine, il existe deux groupes de participants : les créateurs (les usines elles-mêmes) et les produits (les objets créés par les usines). Imaginez la situation : nous avons une usine qui produit des voitures sous la marque AutoRush. Elle sait créer des modèles de voitures avec différents types de carrosseries :
  • berlines
  • breaks
  • coupé
Les choses allaient tellement bien pour nous qu'un beau jour nous avons absorbé le souci de OneAuto. En tant que gestionnaires sensés, nous ne voulons pas perdre les clients OneAuto et notre tâche est de restructurer la production de manière à pouvoir produire :
  • Berlines AutoRush
  • Breaks AutoRush
  • coupé AutoRush
  • Berlines OneAuto
  • Breaks OneAuto
  • coupé OneAuto
Comme vous pouvez le constater, au lieu d'un groupe de produits dérivés, deux sont apparus, qui diffèrent sur certains détails. Le modèle de conception de la méthode d'usine résout le problème de la création de différents groupes de produits, chacun ayant une certaine spécificité. Nous examinerons le principe de ce modèle en pratique, en passant progressivement du simple au complexe, en utilisant l'exemple de notre café, que nous avons créé lors d'une des conférences précédentes .

Un peu sur le modèle d'usine

Je vous le rappelle : nous avons construit avec vous un petit café virtuel. Dans ce document, nous avons appris à créer différents types de café à l'aide d'une simple usine. Aujourd'hui, nous allons affiner cet exemple. Rappelons-nous à quoi ressemblait notre café avec une simple usine. Nous avons eu un cours de café :
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Et aussi plusieurs de ses héritiers - des types de café spécifiques que notre usine a pu produire :
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Pour faciliter l'acceptation des commandes, nous avons mis en place les virements :
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
La fabrique de café elle-même ressemblait à ceci :
public class SimpleCoffeeFactory {
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }

        return coffee;
    }
}
Et enfin, le café lui-même :
public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}

Modernisation d'une usine simple

Notre café se porte bien. À tel point que nous réfléchissons à une expansion. Nous souhaitons ouvrir plusieurs nouveaux points. En tant que gars entreprenants, nous ne produirons pas de cafés monotones. Je veux que chacun ait sa propre touche. Par conséquent, pour commencer, nous ouvrirons deux points : dans les styles italien et américain. Les changements affecteront non seulement l'intérieur, mais aussi les boissons :
  • dans un café italien, nous utiliserons exclusivement des marques de café italiennes, avec une mouture et une torréfaction spéciales.
  • La portion américaine sera un peu plus grande, et à chaque commande nous servirons des guimauves fondues - guimauves.
La seule chose qui restera inchangée est notre modèle économique, qui a fait ses preuves. Si nous parlons en langage codé, voici ce qui se passe. Nous avions 4 classes de produits :
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Et cela devient 8 :
public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Puisque nous souhaitons conserver le modèle économique actuel inchangé, nous souhaitons que la méthode orderCoffee(CoffeeType type)subisse un minimum de changements. Jetons-y un coup d'oeil :
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
    return coffee;
}
Quelles options avons-nous? Nous savons déjà comment écrire une usine, non ? La chose la plus simple qui nous vient immédiatement à l'esprit est d'écrire deux usines similaires, puis de transmettre l'implémentation requise à notre café chez le constructeur. La classe du café ne changera alors pas. Tout d’abord, nous devons créer une nouvelle classe d’usine, hériter de notre usine simple et remplacer le createCoffee (CoffeeType type). Écrivons des usines pour créer du café dans les styles italien et américain :
public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
Nous pouvons maintenant transmettre l'implémentation d'usine requise à CoffeeShop. Voyons à quoi ressemblerait le code pour commander du café dans différents cafés. Par exemple, le cappuccino dans les styles italien et américain :
public class Main {
    public static void main(String[] args) {
        /*
            Закажем капучино в итальянском стиле:
            1. Создадим фабрику для приготовления итальянского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
            3. Закажем наш кофе
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Закажем капучино в американском стиле
            1. Создадим фабрику для приготовления американского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
            3. Закажем наш кофе
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Nous avons créé deux cafés différents, en transférant chacun vers l'usine souhaitée. D'un côté, nous avons atteint notre objectif, mais de l'autre... Quelque chose gratte l'âme irrépressible de l'entrepreneur... Voyons ce qui ne va pas. Premièrement, l’abondance des usines. Est-il possible de créer sa propre usine à chaque fois pour un nouveau point et, en plus, de s'assurer que lors de la création d'un café, l'usine souhaitée soit transférée au constructeur ? Deuxièmement, il s’agit encore d’une simple usine. Juste un peu modernisé. Nous étudions toujours un nouveau modèle ici. Troisièmement, n’est-il pas possible de procéder différemment ? Ce serait cool si nous pouvions localiser toutes les questions sur la préparation du café dans la salle de classe CoffeeShop, en reliant les processus de création du café et de service de la commande, tout en conservant suffisamment de flexibilité pour préparer du café dans différents styles. La réponse est oui, vous le pouvez. C’est ce qu’on appelle le modèle de conception de méthode d’usine.

D'une simple usine à une méthode d'usine

Pour résoudre le problème le plus efficacement possible, nous :
  1. Renvoyons la méthode createCoffee(CoffeeType type)à la classe CoffeeShop.
  2. Rendons cette méthode abstraite.
  3. La classe elle-même CoffeeShopdeviendra abstraite.
  4. La classe CoffeeShopaura des héritiers.
Oui ami. Un café italien n'est rien de plus qu'un héritier de la classe CoffeeShop, mettant en œuvre une méthode createCoffee(CoffeeType type)conforme aux meilleures traditions des baristas italiens. Donc, dans l'ordre. Étape 1. Rendons la classe Coffeeabstraite. Nous disposons désormais de deux familles de produits différents. Les boissons au café italiennes et américaines partagent toujours un ancêtre commun : le Coffee. Il serait correct de le rendre abstrait :
public abstract class Coffee {
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Étape 2. Rendez-le CoffeeShopabstrait, avec une méthode abstraitecreateCoffee(CoffeeType type)
public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Étape 3. Créez un café italien, une classe descendante du café abstrait. Nous y implémentons la méthode createCoffee(CoffeeType type)en tenant compte des spécificités italiennes.
public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Étape 4. Faisons de même pour un café à l'américaine.
public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
Étape 5. Voyons à quoi ressemblerait la commande d'un latte à l'américaine et à l'italienne :
public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
Toutes nos félicitations. Nous venons de mettre en œuvre le modèle de conception de la méthode d'usine dans notre café.

Comment fonctionne la méthode d'usine

Regardons maintenant de plus près ce que nous avons obtenu. Le diagramme ci-dessous montre les classes résultantes. Les blocs verts sont des classes de créateurs, les blocs bleus sont des classes de produits. Modèles de conception : FactoryMethod - 2Quelles conclusions peut-on en tirer ?
  1. Tous les produits sont des implémentations de la classe abstraite Coffee.
  2. Tous les créateurs sont des implémentations de la classe abstraite CoffeeShop.
  3. Nous observons deux hiérarchies de classes parallèles :
    • Hiérarchie des produits. Nous voyons des descendants italiens et des descendants américains
    • Hiérarchie des créateurs. Nous voyons des descendants italiens et des descendants américains
  4. La superclasse CoffeeShopne dispose d'aucune information sur l'implémentation de produit spécifique ( Coffee) qui sera créée.
  5. Une superclasse CoffeeShopdélègue la création d'un produit spécifique à ses descendants.
  6. Chaque classe descendante CoffeeShopimplémente une méthode de fabrique createCoffee()conformément à ses spécificités. En d’autres termes, dans le cadre des implémentations de classes de créateurs, une décision est prise de préparer un produit spécifique basé sur les spécificités de la classe de créateurs.
Vous êtes maintenant prêt à définir le modèle de méthode d'usine . Le modèle de méthode de fabrique définit l'interface de création d'un objet, mais permet aux sous-classes de choisir la classe de l'instance à créer. Ainsi, la méthode Factory délègue l’opération d’instanciation aux sous-classes. En général, se souvenir de la définition n’est pas aussi important que comprendre comment les choses fonctionnent.

Structure de la méthode d'usine

Modèles de conception : FactoryMethod - 3Le diagramme ci-dessus montre la structure générale du modèle de méthode d'usine. Qu’est-ce qui est important ici ?
  1. La classe Creator contient des implémentations de toutes les méthodes qui interagissent avec les produits, à l'exception de la méthode factory.
  2. Une méthode abstraite factoryMethod()doit être implémentée par tous les descendants de la classe Creator.
  3. La classe ConcreteCreatorimplémente une méthode factoryMethod()qui produit directement un produit.
  4. Cette classe est chargée de créer des produits spécifiques. C'est le seul cours contenant des informations sur la création de ces produits.
  5. Tous les produits doivent implémenter une interface commune – être les descendants d’une classe de produits commune. Cela est nécessaire pour que les classes qui utilisent des produits puissent les utiliser au niveau des abstractions plutôt que des implémentations concrètes.

Devoirs

Ainsi, aujourd’hui, nous avons fait beaucoup de travail et étudié le modèle de conception de la méthode d’usine. Il est temps de consolider le matériel que vous avez couvert ! Tâche 1. Travailler à l'ouverture d'un autre café. Il peut être réalisé dans un style anglais ou espagnol. Ou même à la manière d’un vaisseau spatial. Ajoutons du colorant alimentaire au café pour le faire briller, et en général, le café ne sera que de l'espace ! Tâche 2. Lors du dernier cours, vous aviez pour tâche de créer un bar à sushi virtuel ou une pizzeria virtuelle. Votre tâche n'est pas de rester immobile. Aujourd’hui, vous avez appris comment utiliser le modèle de méthode d’usine pour réussir. Il est temps de profiter de ces connaissances et de développer votre propre entreprise ;)
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION