Modèles de conception: méthode Factory vs Factory vs Abstract Factory

183

Je lisais des modèles de conception sur un site Web

Là, j'ai lu sur Factory, la méthode Factory et l'usine abstraite mais ils sont tellement déroutants, je ne suis pas clair sur la définition. Selon les définitions

Factory - Crée des objets sans exposer la logique d'instanciation au client et fait référence à l'objet nouvellement créé via une interface commune. Est une version simplifiée de la méthode d'usine

Méthode Factory - Définit une interface pour créer des objets, mais laisse les sous-classes décider quelle classe instancier et fait référence à l'objet nouvellement créé via une interface commune.

Abstract Factory - Offre l'interface pour créer une famille d'objets liés, sans spécifier explicitement leurs classes.

J'ai également regardé les autres threads de stackoverflow concernant Abstract Factory vs Factory Method, mais les diagrammes UML qui y sont dessinés rendent ma compréhension encore pire.

Quelqu'un peut-il me dire

  1. En quoi ces trois modèles sont-ils différents les uns des autres?
  2. Quand utiliser lequel?
  3. Et aussi si possible, des exemples java liés à ces modèles?
Just_another_developer
la source
3
Alors que je cherchais des réponses à peu près à la même question que l'OP, j'ai trouvé cet article: From No Factory to Factory Method . Il donne un aperçu en suivant l'évolution d'un exemple de projet (la méthode d'usine mentionnée dans le titre est l'une des étapes évolutives).
Nick Alexeev

Réponses:

251

Les trois types d'usine font la même chose: ils sont un "constructeur intelligent".

Disons que vous voulez pouvoir créer deux types de fruits: la pomme et l'orange.

Usine

Factory est "fixe", en ce sens que vous n'avez qu'une seule implémentation sans sous-classification. Dans ce cas, vous aurez une classe comme celle-ci:

class FruitFactory {

  public Apple makeApple() {
    // Code for creating an Apple here.
  }

  public Orange makeOrange() {
    // Code for creating an orange here.
  }

}

Cas d'utilisation: Construire un Apple ou un Orange est un peu trop complexe à gérer dans le constructeur pour l'un ou l'autre.

Méthode d'usine

La méthode d'usine est généralement utilisée lorsque vous avez un traitement générique dans une classe, mais que vous voulez varier le type de fruit que vous utilisez réellement. Alors:

abstract class FruitPicker {

  protected abstract Fruit makeFruit();

  public void pickFruit() {
    private final Fruit f = makeFruit(); // The fruit we will work on..
    <bla bla bla>
  }
}

... alors vous pouvez réutiliser la fonctionnalité commune en FruitPicker.pickFruit()implémentant une méthode de fabrique dans les sous-classes:

class OrangePicker extends FruitPicker {

  @Override
  protected Fruit makeFruit() {
    return new Orange();
  }
}

Usine abstraite

La fabrique abstraite est normalement utilisée pour des choses comme l'injection de dépendances / la stratégie, quand vous voulez être capable de créer toute une famille d'objets qui doivent être du "même genre" et avoir des classes de base communes. Voici un exemple vaguement lié aux fruits. Le cas d'utilisation ici est que nous voulons nous assurer de ne pas utiliser accidentellement un OrangePicker sur un Apple. Tant que nous obtenons nos fruits et notre cueilleur de la même usine, ils correspondent.

interface PlantFactory {

  Plant makePlant();

  Picker makePicker(); 

}

public class AppleFactory implements PlantFactory {
  Plant makePlant() {
    return new Apple();
  }

  Picker makePicker() {
    return new ApplePicker();
  }
}

public class OrangeFactory implements PlantFactory {
  Plant makePlant() {
    return new Orange();
  }

  Picker makePicker() {
    return new OrangePicker();
  }
}
Anders Johansen
la source
8
+1 C'est la réponse qui ressemble le plus à ma compréhension de ces modèles. L'ajout d'exemples de code d'appel (Client) aiderait également? La question qui me dérange beaucoup est la suivante: pouvons-nous dire que Abstract Factory Pattern est juste Factory étendu avec Factory Method Pattern (si cela est vrai, je suis clair sur ce sujet)?
croraf
9
C'est l'exemple que j'ai cherché pendant des années.
Taztingo
C'est une explication fantastique! Merci!
André Andrade le
@ AndréAndrade Comment invoquer la méthode Factory ? Un petit échantillon de code pls :) Cela effacera mon doute sur son utilisation
Arnab Dutta
25
  1. En quoi ces trois modèles sont-ils différents les uns des autres?

Factory: crée des objets sans exposer la logique d'instanciation au client.

Méthode d'usine: définissez une interface pour créer un objet, mais laissez les sous-classes décider de la classe à instancier. La méthode Factory permet à une classe de différer l'instanciation aux sous-classes

Abstract Factory: fournit une interface pour créer des familles d'objets liés ou dépendants sans spécifier leurs classes concrètes.

Le modèle AbstractFactory utilise la composition pour déléguer la responsabilité de la création d'un objet à une autre classe tandis que le modèle de conception de la méthode Factory utilise l'héritage et s'appuie sur une classe ou une sous-classe dérivée pour créer un objet

  1. Quand utiliser lequel?

Factory: Le client a juste besoin d'une classe et ne se soucie pas de l'implémentation concrète qu'il obtient.

Méthode d'usine: le client ne sait pas quelles classes concrètes il devra créer au moment de l'exécution, mais veut juste obtenir une classe qui fera le travail.

AbstactFactory: lorsque votre système doit créer plusieurs familles de produits ou que vous souhaitez fournir une bibliothèque de produits sans exposer les détails d'implémentation.

Les classes Abstract Factory sont souvent implémentées avec la méthode Factory. Les méthodes d'usine sont généralement appelées dans les méthodes de modèle.

  1. Et aussi si possible, des exemples java liés à ces modèles?

Usine et méthode d'usine

Intention:

Définissez une interface pour créer un objet, mais laissez les sous-classes décider quelle classe instancier. La méthode Factory permet à une classe de différer l'instanciation aux sous-classes.

Diagramme UML :

entrez la description de l'image ici

Produit: il définit une interface des objets créés par la méthode Factory.

ConcreteProduct: Implémente l'interface produit

Créateur: déclare la méthode Factory

ConcreateCreator: implémente la méthode Factory pour renvoyer une instance d'un ConcreteProduct

Énoncé du problème: créez une usine de jeux à l'aide des méthodes d'usine, qui définissent l'interface du jeu.

Extrait de code:

Modèle d'usine. Quand utiliser les méthodes d'usine?

Comparaison avec d'autres modèles de création:

  1. La conception commence à l'aide de la méthode d'usine (moins compliquée, plus personnalisable, les sous-classes prolifèrent) et évolue vers Abstract Factory, Prototype ou Builder (plus flexible, plus complexe) à mesure que le concepteur découvre où plus de flexibilité est nécessaire

  2. Les classes Abstract Factory sont souvent implémentées avec des méthodes Factory , mais elles peuvent également être implémentées à l'aide de Prototype

Références pour en savoir plus: Modèles de conception Sourcemaking

Ravindra babu
la source
Pour la méthode d'usine, ne devrait-il pas être défini comme une superclasse?
Tony Lin
21

Factory - Classe Factory séparée pour créer un objet complexe.

Ex: Classe FruitFactory pour créer un objet Fruit

class FruitFactory{

public static Fruit getFruit(){...}

}

Méthode d'usine - Au lieu d'une classe entière séparée pour l'usine, ajoutez simplement une méthode dans cette classe elle-même en tant que fabrique.

Ex:

Calendar.getInstance() (Java's Calendar)

Méthode d'usine abstraite - Usine d'usine

Ex: Disons que nous voulons construire une usine pour les pièces d'ordinateurs. Il existe donc plusieurs types d'ordinateurs comme les ordinateurs portables, les ordinateurs de bureau, les serveurs.

Donc, pour chaque type de ordinateur, nous avons besoin d'une usine. Nous créons donc une usine d'usines de haut niveau comme ci-dessous

ComputerTypeAbstractFactory.getComputerPartFactory(String computerType) ---> This will return PartFactory which can be one of these ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

Maintenant, ces 3 eux-mêmes sont à nouveau des usines. (Vous aurez affaire à PartFactory lui-même, mais sous le capot, il y aura une implémentation distincte basée sur ce que vous avez fourni dans abstract factory)

  Interface-> PartFactory. getComputerPart(String s), 
Implementations -> ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

Usage:
new ComputerTypeAbstractFactory().getFactory(“Laptop”).getComputerPart(“RAM”)

EDIT: édité pour fournir des interfaces exactes pour Abstract Factory conformément aux objections dans les commentaires.

Ravi K
la source
1
L'idée derrière le ComputerFactoryserait que vous ayez une interface de création commune ( getScreen(); getKeyboard(); getDiskdrive(); ...), pas une interface par type d'ordinateur comme vous le suggérez. Vous pouvez sentir un problème de conception si vous utilisez le même mot deux fois dans la même déclaration: Laptop Factory.get Laptop Part ().
xtofl
Non non non, n'allez pas exactement sur le code lui-même. C'était juste une anologie pour comprendre. Si vous voulez un exemple exact avec des interfaces, le voici. Objets: Interface -> ComputerPart, Implementation -> RAM, HDD, Processor Factory: Interface-> PartFactory. getComputerPart (String s), Implémentations -> ServerPartFactory, LaptopPartFactory, DesktopPartFactory. Abstract Factory: ComputerType.getPartFactory ("String s") Utilisation: new ComputerType (). GetFactory ("Laptop"). GetComputerPart ("RAM")
Ravi K
2
J'ai mis à jour la réponse pour prendre soin de votre préoccupation. En fait, l'usine abstraite n'est rien d'autre qu'une usine d'usine. J'ai donné l'exemple précédent juste pour référence (en supposant que les lecteurs prendront soin des interfaces pendant la mise en œuvre réelle). Merci encore de l'avoir notifié. C'est toujours bon de s'améliorer. :)
Ravi K
Non abstract Factoryn'est pas une usine d'usine ... C'est un abstract classou un interfaceobjet capable de créer qui sera implémenté / étendu avec différentes usines de béton. Voir la réponse acceptée pour les détails du code. Et veuillez supprimer ou modifier votre réponse en conséquence.
Julien__
J'aime l'explication de la méthode d'usine, qui est suffisamment concise pour dévoiler pourquoi elle est ainsi nommée. Dans ce modèle, la fabrique est la méthode, PAS la classe qui n'est généralement pas un utilitaire d'assistance regroupant des méthodes d'instanciation mais qui a un sens en soi. D'autres réponses plus élaborées ont malheureusement manqué ce point.
wlnirvana
11

Chaque modèle de conception se développe pour aider à garantir que le code écrit et fonctionnel n'est pas touché. Nous savons tous qu'une fois que nous touchons le code de travail, il y a des défauts dans les flux de travail existants, et beaucoup plus de tests doivent être effectués pour s'assurer que nous n'avons rien cassé.

Un modèle de fabrique crée des objets basés sur des critères d'entrée, garantissant ainsi que vous n'avez pas besoin d'écrire du code comme si c'était alors créer ce genre d'objet sinon ce genre d'objet. Un bon exemple de ceci est un site Web de voyage. Un site Web de voyages ne peut fournir que des voyages (vol, train, bus) ou / et fournir des hôtels ou / et fournir des forfaits d'attractions touristiques. Désormais, lorsqu'un utilisateur choisit ensuite, le site Web doit décider quels objets il doit créer. Devrait-il créer uniquement l'objet de voyage ou d'hôtel.

Maintenant, si vous envisagez d'ajouter un autre site Web à votre portefeuille et que vous pensez que le même noyau sera utilisé, par exemple, un site Web de covoiturage, qui recherche maintenant des taxis et effectue des paiements en ligne, vous pouvez utiliser une usine abstraite à votre base. De cette façon, vous pouvez simplement installer une autre usine de taxis et de covoiturage.

Les deux usines n'ont rien à voir l'une avec l'autre, c'est donc une bonne conception de les conserver dans des usines différentes.

J'espère que c'est clair maintenant. Étudiez à nouveau le site Web en gardant cet exemple à l'esprit, j'espère que cela vous aidera. Et j'espère vraiment avoir représenté les motifs correctement :).

Siddharth
la source
3

Pour cette réponse, je me réfère au livre "Gang of Four".

Il n'y a pas de définitions "Factory", "Simple Factory" ou "Virtual Factory" dans le livre. Habituellement, quand les gens parlent du modèle "Factory", ils peuvent parler de quelque chose qui crée un objet particulier d'une classe (mais pas le modèle "constructeur"); ils peuvent ou non faire référence aux modèles "Factory Method" ou "Abstract Factory". N'importe qui peut implémenter "Factory" comme il ne le fera pas car ce n'est pas un terme formel (gardez à l'esprit que certaines personnes \ entreprises \ communautés peuvent avoir leur propre vocabulaire).

Le livre ne contient que des définitions pour «Abstract Factory» et «Factory Method».

Voici les définitions du livre et une brève explication des raisons pour lesquelles les deux peuvent être si déroutants. J'omets les exemples de code car vous pouvez les trouver dans d'autres réponses:

Méthode d'usine (GOF) : définissez une interface pour créer un objet, mais laissez les sous-classes décider de la classe à instancier. La méthode Factory permet à une classe de différer l'instanciation aux sous-classes.

Abstract Factory (GOF) : Fournit une interface pour créer des familles d'objets liés ou dépendants sans spécifier leurs classes concrètes.

Source de confusion : Souvent, on peut appeler une classe utilisée dans le modèle "Factory Method" comme "Factory". Cette classe est abstraite par définition. C'est pourquoi il est facile d'appeler cette classe "Abstract Factory". Mais c'est juste le nom de la classe; vous ne devriez pas le confondre avec le modèle "Abstract Factory" (nom de classe! = nom du modèle). Le modèle "Abstract Factory" est différent - il n'utilise pas de classe abstraite; il définit une interface (pas nécessairement une interface de langage de programmation) pour créer des parties d'un objet plus grand ou des objets qui sont liés les uns aux autres ou doivent être créés d'une manière particulière.

Pavel Sapehin
la source
2
AbstractProductA, A1 and A2 both implementing the AbstractProductA
AbstractProductB, B1 and B2 both implementing the AbstractProductB

interface Factory {
    AbstractProductA getProductA(); //Factory Method - generate A1/A2
}

En utilisant la méthode d'usine, l'utilisateur peut créer A1 ou A2 de AbstractProductA.

interface AbstractFactory {
    AbstractProductA getProductA(); //Factory Method
    AbstractProductB getProductB(); //Factory Method
}

Mais Abstract Factory ayant plus d'une méthode d'usine (ex: 2 méthodes d'usine), en utilisant ces méthodes d'usine, elle créera l'ensemble des objets / objets associés. En utilisant Abstract Factory, l'utilisateur peut créer des objets A1, B1 de AbstractProductA, AbstractProductB

rameshvanka
la source
0

Personne n'a cité le livre original Design Patterns: Elements of Reusable Object-Oriented Software , qui donne la réponse dans les deux premiers paragraphes de la section «Discussion of Creational Patterns» (c'est moi qui souligne):

Il existe deux manières courantes de paramétrer un système par les classes d'objets qu'il crée. Une façon consiste à sous -classer la classe qui crée les objets; cela correspond à l'utilisation du modèle Factory Method (107). Le principal inconvénient de cette approche est qu'elle peut nécessiter une nouvelle sous-classe juste pour changer la classe du produit. De tels changements peuvent en cascade. Par exemple, lorsque le créateur du produit est lui-même créé par une méthode d'usine, vous devez également remplacer son créateur.

L'autre façon de paramétrer un système repose davantage sur la composition d' objets : définissez un objet qui est responsable de la connaissance de la classe des objets produit et en faites un paramètre du système. C'est un aspect clé des modèles Abstract Factory (87), Builder (97) et Prototype (117). Les trois impliquent la création d'un nouvel «objet usine» dont la responsabilité est de créer des objets produit. Abstract Factory a l'objet d'usine produisant des objets de plusieurs classes. Builder permet à l'objet usine de créer un produit complexe de manière incrémentielle à l'aide d'un protocole complexe correspondant. Prototype permet à l'objet usine de créer un produit en copiant un objet prototype. Dans ce cas, l'objet d'usine et le prototype sont le même objet, car le prototype est responsable du retour du produit.

Maggyero
la source