Salut! Aujourd'hui, nous allons continuer à étudier les modèles de conception et nous discuterons du modèle d'usine abstraite .
Voici ce que nous allons couvrir dans la leçon :
Nous allons maintenant écrire du code conformément à ces instructions :

- Nous discuterons de ce qu'est une usine abstraite et du problème que ce modèle résout
- Nous allons créer le squelette d'une application multiplateforme pour commander du café via une interface utilisateur
- Nous étudierons des instructions sur la façon d'utiliser ce modèle, y compris en regardant un diagramme et un code
- Et en prime, cette leçon comprend un œuf de Pâques caché qui vous aidera à apprendre à utiliser Java pour déterminer le nom du système d'exploitation et, selon le résultat, à effectuer une autre action ou une autre.
- héritage en Java
- classes abstraites et méthodes en Java
Quels problèmes une usine abstraite résout-elle ?
Une usine abstraite, comme tous les modèles d'usine, nous aide à nous assurer que de nouveaux objets sont créés correctement. Nous l'utilisons pour gérer la "production" de différentes familles d'objets interconnectés. Diverses familles d'objets interconnectés... Qu'est-ce que cela signifie ? Rassurez-vous : en pratique, tout est plus simple qu'il n'y paraît. Pour commencer, que pourrait être une famille d'objets interconnectés ? Supposons que nous développions une stratégie militaire impliquant plusieurs types d'unités :- infanterie
- cavalerie
- archers
Continuons à automatiser notre café
Dans la dernière leçon, nous avons étudié le modèle de méthode d'usine. Nous l'avons utilisé pour développer notre activité de café et ouvrir plusieurs nouveaux sites. Aujourd'hui, nous allons continuer à moderniser notre entreprise. En utilisant le modèle d'usine abstraite, nous allons jeter les bases d'une nouvelle application de bureau pour commander du café en ligne. Lors de l'écriture d'une application de bureau, nous devons toujours penser au support multiplateforme. Notre application doit fonctionner à la fois sur macOS et Windows (spoiler : la prise en charge de Linux est laissée à votre devoir). A quoi ressemblera notre application ? Assez simple : ce sera un formulaire composé d'un champ de texte, d'un champ de sélection et d'un bouton. Si vous avez de l'expérience avec différents systèmes d'exploitation, vous avez certainement remarqué que les boutons sous Windows sont rendus différemment que sur un Mac. Comme tout le reste... Eh bien, commençons.- boutons
- champs de texte
- champs de sélection
onClick
, onValueChanged
ou onInputChanged
. En d'autres termes, nous pourrions définir des méthodes qui nous permettront de gérer divers événements (appuyer sur un bouton, saisir du texte, sélectionner une valeur dans une boîte de sélection). Tout ceci est volontairement omis ici afin de ne pas surcharger l'exemple et de le rendre plus clair au fur et à mesure de l'étude du modèle d'usine. Définissons des interfaces abstraites pour nos produits :
public interface Button {}
public interface Select {}
public interface TextField {}
Pour chaque système d'exploitation, nous devons créer des éléments d'interface dans le style du système d'exploitation. Nous allons écrire du code pour Windows et MacOS. Créons des implémentations pour Windows :
public class WindowsButton implements Button {
}
public class WindowsSelect implements Select {
}
public class WindowsTextField implements TextField {
}
Maintenant, nous faisons la même chose pour MacOS :
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Excellent. Nous pouvons maintenant passer à notre usine abstraite, qui créera tous les types de produits abstraits disponibles :
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Superbe. Comme vous pouvez le voir, nous n'avons encore rien fait de compliqué. Tout ce qui suit est également simple. Par analogie avec les produits, nous créons différentes implémentations d'usine pour chaque OS. Commençons par Windows :
public class WindowsGUIFactory implements GUIFactory {
public WindowsGUIFactory() {
System.out.println("Creating GUIFactory for Windows OS");
}
public Button createButton() {
System.out.println("Creating Button for Windows OS");
return new WindowsButton();
}
public TextField createTextField() {
System.out.println("Creating TextField for Windows OS");
return new WindowsTextField();
}
public Select createSelect() {
System.out.println("Creating Select for Windows OS");
return new WindowsSelect();
}
}
Nous avons ajouté une sortie de console à l'intérieur des méthodes et du constructeur afin d'illustrer davantage ce qui se passe. Maintenant pour macOS :
public class MacGUIFactory implements GUIFactory {
public MacGUIFactory() {
System.out.println("Creating GUIFactory for macOS");
}
@Override
public Button createButton() {
System.out.println("Creating Button for macOS");
return new MacButton();
}
@Override
public TextField createTextField() {
System.out.println("Creating TextField for macOS");
return new MacTextField();
}
@Override
public Select createSelect() {
System.out.println("Creating Select for macOS");
return new MacSelect();
}
}
Notez que chaque signature de méthode indique que la méthode renvoie un type abstrait. Mais à l'intérieur des méthodes, nous créons des implémentations spécifiques des produits. C'est le seul endroit où nous contrôlons la création d'instances spécifiques. Il est maintenant temps d'écrire une classe pour le formulaire. Il s'agit d'une classe Java dont les champs sont des éléments d'interface :
public class CoffeeOrderForm {
private final TextField customerNameTextField;
private final Select coffeeTypeSelect;
private final Button orderButton;
public CoffeeOrderForm(GUIFactory factory) {
System.out.println("Creating coffee order form");
customerNameTextField = factory.createTextField();
coffeeTypeSelect = factory.createSelect();
orderButton = factory.createButton();
}
}
Une fabrique abstraite qui crée des éléments d'interface est transmise au constructeur du formulaire. Nous transmettrons l'implémentation d'usine nécessaire au constructeur afin de créer des éléments d'interface pour un système d'exploitation particulier.
public class Application {
private CoffeeOrderForm coffeeOrderForm;
public void drawCoffeeOrderForm() {
// Determine the name of the operating system through System.getProperty()
String osName = System.getProperty("os.name").toLowerCase();
GUIFactory guiFactory;
if (osName.startsWith("win")) { // For Windows
guiFactory = new WindowsGUIFactory();
} else if (osName.startsWith("mac")) { // For Mac
guiFactory = new MacGUIFactory();
} else {
System.out.println("Unknown OS. Unable to draw form :(");
return;
}
coffeeOrderForm = new CoffeeOrderForm(guiFactory);
}
public static void main(String[] args) {
Application application = new Application();
application.drawCoffeeOrderForm();
}
}
Si nous exécutons l'application sous Windows, nous obtenons le résultat suivant :
Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Sur un Mac, la sortie sera la suivante :
Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
Sous Linux :
Unknown OS. Unable to draw form :(
Et maintenant on résume. Nous avons écrit le squelette d'une application basée sur une interface graphique dans laquelle les éléments d'interface sont créés spécifiquement pour le système d'exploitation concerné. Nous allons répéter de manière concise ce que nous avons créé :
- Une famille de produits composée d'un champ de saisie, d'un champ de sélection et d'un bouton.
- Différentes implémentations de la famille de produits pour Windows et macOS.
- Une usine abstraite qui définit une interface pour créer nos produits.
- Deux implémentations de notre usine, chacune responsable de la création d'une famille spécifique de produits.
- Un formulaire (une classe Java) dont les champs sont des éléments d'interface abstraits qui sont initialisés avec les valeurs nécessaires dans le constructeur à l'aide d'une usine abstraite.
- Classe d'application Dans cette classe, nous créons un formulaire, en passant l'implémentation d'usine souhaitée à son constructeur.
Usine abstraite : mode d'emploi
Une usine abstraite est un modèle de conception permettant de gérer la création de différentes familles de produits sans être lié à des classes de produits concrètes. Lorsque vous utilisez ce modèle, vous devez :- Définir des familles de produits. Supposons que nous en ayons deux :
SpecificProductA1
,SpecificProductB1
SpecificProductA2
,SpecificProductB2
- Pour chaque produit de la famille, définissez une classe abstraite (interface). Dans notre cas, nous avons :
ProductA
ProductB
- Au sein de chaque famille de produits, chaque produit doit implémenter l'interface définie à l'étape 2.
- Créez une usine abstraite, avec des méthodes pour créer chaque produit défini à l'étape 2. Dans notre cas, ces méthodes seront :
ProductA createProductA();
ProductB createProductB();
- Créez des implémentations d'usines abstraites afin que chaque implémentation contrôle la création de produits d'une seule famille. Pour ce faire, à l'intérieur de chaque implémentation de la fabrique abstraite, vous devez implémenter toutes les méthodes de création afin qu'elles créent et renvoient des implémentations de produit spécifiques.

// Define common product interfaces
public interface ProductA {}
public interface ProductB {}
// Create various implementations (families) of our products
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}
public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}
// Create an abstract factory
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// Implement the abstract factory in order to create products in family 1
public class SpecificFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA1();
}
@Override
public ProductB createProductB() {
return new SpecificProductB1();
}
}
// Implement the abstract factory in order to create products in family 2
public class SpecificFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA2();
}
@Override
public ProductB createProductB() {
return new SpecificProductB2();
}
}
Devoirs
Pour renforcer le matériau, vous pouvez faire 2 choses :- Affinez l'application de commande de café pour qu'elle fonctionne également sous Linux.
- Créez votre propre usine abstraite pour produire des unités impliquées dans n'importe quelle stratégie militaire. Cela peut être soit une stratégie militaire historique impliquant de vraies armées, soit une stratégie fantastique avec des orcs, des gnomes et des elfes. L'important est de choisir quelque chose qui vous intéresse. Soyez créatif, imprimez des messages sur la console et amusez-vous à apprendre les motifs !
GO TO FULL VERSION