Bonjour! Aujourd'hui, nous continuerons à étudier les modèles de conception et à parler de l'usine abstraite . Ce que nous ferons pendant la conférence :
- Discutons de ce qu'est une usine abstraite et du problème que ce modèle résout ;
- nous créerons le cadre d'une application multiplateforme pour commander du café avec une interface utilisateur ;
- Étudions les instructions d'utilisation de ce modèle avec un diagramme et un code ;
- En prime, il y a un œuf de Pâques caché dans la conférence, grâce auquel vous apprendrez à déterminer le nom du système d'exploitation utilisant Java et, en fonction du résultat, à effectuer l'une ou l'autre action.
- héritage en Java ;
- classes et méthodes abstraites en Java.
Quels problèmes le modèle d’usine abstrait résout-il ?
L’usine abstraite, comme tous les modèles d’usine, nous aide à organiser correctement la création de nouveaux objets. Avec son aide, nous gérons la « libération » de différentes familles d'objets interconnectés. Différentes familles d'objets interdépendants... Qu'est-ce que c'est ? Ne vous inquiétez pas : en pratique, tout est plus simple qu'il n'y paraît. Commençons par ce que pourrait être une famille d’objets liés ? Supposons que vous et moi développions une stratégie et qu'elle contienne plusieurs unités de combat :- infanterie;
- cavalerie;
- archers.
Nous continuons à automatiser le café
Lors de la dernière conférence, nous avons étudié le modèle de méthode d'usine, grâce auquel nous avons pu développer le commerce du café et ouvrir plusieurs nouveaux points de vente de café. Aujourd'hui, nous allons poursuivre notre travail de modernisation de notre entreprise. En utilisant le modèle d'usine abstrait, nous poserons les bases d'une nouvelle application de bureau permettant de commander du café en ligne. Lorsque nous écrivons une application pour le bureau, nous devons toujours penser au multiplateforme. Notre application devrait fonctionner à la fois sur macOS et Windows (spoiler : Linux vous sera laissé en devoir). À quoi ressemblera notre candidature ? 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 sous Windows, les boutons sont rendus différemment que sur Mac. Comme tout le reste... Alors, commençons. Dans le rôle des familles de produits, comme vous l'avez probablement déjà compris, nous aurons des éléments d'interface graphique :- boutons;
- champs de texte ;
- champs de sélection.
onClick
, onValueChanged
ou onInputChanged
. Ceux. des méthodes qui nous permettront de gérer divers événements (cliquer sur un bouton, saisir du texte, sélectionner une valeur dans une zone de sélection). Tout cela est volontairement omis afin de ne pas surcharger l'exemple et de le rendre plus visuel pour 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 de ce système d'exploitation. Nous écrivons 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, pareil pour MacOS :
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Super. Nous pouvons maintenant démarrer notre usine abstraite, qui créera tous les types de produits abstraits existants :
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Parfait. Comme vous pouvez le constater, rien de compliqué jusqu’à présent. Ensuite, tout est tout aussi simple. Par analogie avec les produits, nous créons différentes implémentations de notre usine pour chaque OS. Commençons par Windows :
public class WindowsGUIFactory implements GUIFactory {
public WindowsGUIFactory() {
System.out.println("Creating gui factory 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();
}
}
La sortie de la console dans les méthodes et les constructeurs a été ajoutée pour démontrer davantage son fonctionnement. Maintenant pour macOS :
public class MacGUIFactory implements GUIFactory {
public MacGUIFactory() {
System.out.println("Creating gui factory 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();
}
}
Remarque : chaque méthode, selon sa signature, renvoie un type abstrait. Mais à l’intérieur de la méthode, nous créons une mise en œuvre concrète du produit. C'est le seul endroit où nous contrôlons la création d'instances spécifiques. Il est maintenant temps d'écrire la classe form. Il s'agit d'une classe Java dont les champs sont des éléments d'interface :
public class OrderCoffeeForm {
private final TextField customerNameTextField;
private final Select coffeTypeSelect;
private final Button orderButton;
public OrderCoffeeForm(GUIFactory factory) {
System.out.println("Creating order coffee form");
customerNameTextField = factory.createTextField();
coffeTypeSelect = factory.createSelect();
orderButton = factory.createButton();
}
}
Une fabrique abstraite est transmise au constructeur de formulaire, qui crée des éléments d'interface. Nous transmettrons l'implémentation d'usine requise au constructeur afin que nous puissions créer des éléments d'interface pour un système d'exploitation particulier.
public class Application {
private OrderCoffeeForm orderCoffeeForm;
public void drawOrderCoffeeForm() {
// Определим Name операционной системы, получив meaning системной проперти через System.getProperty
String osName = System.getProperty("os.name").toLowerCase();
GUIFactory guiFactory;
if (osName.startsWith("win")) { // Для windows
guiFactory = new WindowsGUIFactory();
} else if (osName.startsWith("mac")) { // Для mac
guiFactory = new MacGUIFactory();
} else {
System.out.println("Unknown OS, can't draw form :( ");
return;
}
orderCoffeeForm = new OrderCoffeeForm(guiFactory);
}
public static void main(String[] args) {
Application application = new Application();
application.drawOrderCoffeeForm();
}
}
Si nous exécutons l'application sous Windows, nous obtiendrons le résultat suivant :
Creating gui factory for Windows OS
Creating order coffee form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
Sur un Mac, le résultat sera le suivant :
Creating gui factory for macOS
Creating order coffee form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
Sous Linux :
Unknown OS, can't draw form :(
Eh bien, vous et moi tirons la conclusion suivante. Nous avons écrit un framework pour une application GUI qui crée exactement les éléments d'interface appropriés pour un système d'exploitation donné. Répétons brièvement ce que nous avons créé :
- Famille de produits : champ de saisie, champ de sélection et bouton.
- Diverses implémentations de cette famille de produits, pour Windows et macOS.
- Une usine abstraite, au sein de laquelle nous avons défini l'interface de création de nos produits.
- Deux implémentations de notre usine, chacune étant chargée de créer 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 dans le constructeur avec les valeurs requises à l'aide d'une usine abstraite.
- Classe d'application. À l'intérieur, nous créons un formulaire avec lequel nous transmettons l'implémentation requise de notre usine au constructeur.
Usine abstraite : mode d’emploi
Abstract Factory 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 spécifiques. Lorsque vous utilisez ce modèle, vous devez :- Définir eux-mêmes les 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 c'est :
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 de création pour chaque produit défini à l'étape 2. Dans notre cas, ces méthodes seront :
ProductA createProductA();
ProductB createProductB();
- Créez des implémentations de la fabrique abstraite afin que chaque implémentation contrôle la création de produits de la même famille. Pour ce faire, à l'intérieur de chaque implémentation de la fabrique abstraite, il est nécessaire d'implémenter toutes les méthodes de création, afin que des implémentations concrètes de produits soient créées et renvoyées à l'intérieur.
// Определим общие интерфейсы продуктов
public interface ProductA {}
public interface ProductB {}
// Создадим различные реализации (семейства) наших продуктов
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}
public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}
// Создадим абстрактную фабрику
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA1();
}
@Override
public ProductB createProductB() {
return new SpecificProductB1();
}
}
// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA2();
}
@Override
public ProductB createProductB() {
return new SpecificProductB2();
}
}
Devoirs
Pour consolider le matériel, vous pouvez faire 2 choses :- Améliorez l'application de commande de café pour qu'elle fonctionne sous Linux.
- Créez votre propre usine abstraite pour produire des unités de n'importe quelle stratégie. Cela peut être soit une stratégie historique avec de vraies armées, soit une stratégie fantastique avec des orcs, des nains et des elfes. L'essentiel est que vous trouviez cela intéressant. Faites preuve de créativité, publiez des épingles sur la console et amusez-vous à apprendre les modèles !
GO TO FULL VERSION