Classes d'usine [fermé]


Personnellement, je n'ai jamais compris l'idée des classes d'usine car il semble beaucoup plus utile de simplement instancier un objet directement. Ma question est simple, dans quelle situation l'utilisation d'un modèle de classe d'usine est-elle la meilleure option, pour quelle raison, et à quoi ressemble une bonne classe d'usine?

Author: sepp2k, 2013-01-29

7 answers

L'idée ici est la séparation des préoccupations: Si le code qui utilise l'objet a également suffisamment de renseignements pour l'instancier, vous n'avez pas besoin d'une usine. Cependant, s'il y a une logique ou une configuration impliquée à laquelle vous ne voulez pas que l'utilisateur de l'API pense (ou joue avec), vous pouvez masquer tout cela (et l'encapsuler pour la réutilisation) dans une usine.

Voici un exemple: Vous souhaitez accéder à l'un des services fournis par Google App Engine. Le même code devrait fonctionner à la fois environnement de développement (dont il existe deux versions, maître-esclave et de haute disponibilité) et complètement différents de développement local environnement. Google ne veut pas vous parler du fonctionnement interne de leur infrastructure interne, et vous ne voulez pas vraiment savoir. Donc, ce qu'ils font, c'est fournir des interfaces et des usines (et plusieurs implémentations de ces interfaces pour que les usines choisissent parmi lesquelles vous n'avez même pas besoin de savoir).

 26
Author: Thilo, 2013-01-29 04:06:02

Voici une véritable usine en direct de ma base de code. Il est utilisé pour générer une classe d'échantillonneur qui sait comment échantillonner des données à partir d'un ensemble de données (il est à l'origine en C#, donc excusez tout faux-pas java)

class SamplerFactory
{
  private static Hashtable<SamplingType, ISampler> samplers;

  static
  {
    samplers = new Hashtable<SamplingType, ISampler>();
    samplers.put(SamplingType.Scalar, new ScalarSampler());
    samplers.put(SamplingType.Vector, new VectorSampler());
    samplers.put(SamplingType.Array, new ArraySampler());
  }

  public static ISampler GetSampler(SamplingType samplingType)
  {
    if (!samplers.containsKey(samplingType))
      throw new IllegalArgumentException("Invalid sampling type or sampler not initialized");
    return samplers.get(samplingType);
  }
}

Et voici un exemple d'utilisation:

ISampler sampler = SamplerFactory.GetSampler(SamplingType.Array);
dataSet = sampler.Sample(dataSet);

Comme vous le voyez, ce n'est pas beaucoup de code, et il pourrait même être plus court et plus rapide juste à faire

ArraySampler sampler = new ArraySampler();
dataSet = sampler.Sample(dataSet);

Que d'utiliser l'usine. Alors pourquoi ai-je même pas la peine? Eh bien, il y a deux raisons fondamentales, qui s'appuient sur chacune autres:

  1. Tout d'Abord, c'est la simplicité et la maintenabilité du code. Disons que dans le code appelant, le enum est fourni en tant que paramètre. C'est-à-dire si j'avais une méthode qui doit traiter les données, y compris l'échantillonnage, je peux écrire:

    void ProcessData(Object dataSet, SamplingType sampling)
    {
      //do something with data
      ISampler sampler = SamplerFactory.GetSampler(sampling);
      dataSet= sampler.Sample(dataSet);
      //do something other with data
    }
    

    Au lieu d'une construction plus lourde, comme ceci:

    void ProcessData(Object dataSet, SamplingType sampling)
    {
      //do something with data
      ISampler sampler;
      switch (sampling) {
        case SamplingType.Scalar:  
          sampler= new ScalarSampler();
          break;
        case SamplingType.Vector:  
          sampler= new VectorSampler();
          break;
        case SamplingType.Array:
          sampler= new ArraySampler();
          break;
        default:
          throw new IllegalArgumentException("Invalid sampling type");
      }
      dataSet= sampler.Sample(dataSet);
      //do something other with data
    }
    

    Notez que cette monstruosité devrait être écrite chaque fois que j'ai besoin de moi un échantillon. Et vous pouvez imaginer à quel point il sera amusant de changer si, allons disons que j'ai ajouté un paramètre au constructeur ScalarSampler ou ajouté un nouveau SamplingType. Et cette usine n'a plus que trois options, imaginez une usine avec 20 implémentations.

  2. Deuxièmement, c'est le découplage du code. Lorsque j'utilise une usine, le code appelant ne savent ou doivent savoir qu'une classe appelée ArraySampler existe même. La classe pourrait même être résolue au moment de l'exécution, et le site d'appel ne serait pas le plus sage. Donc, par conséquent, je suis libre de changer la classe ArraySampler autant que je veux, y compris, mais sans s'y limiter, la suppression pure et simple de la classe, si, par exemple, je décide que le ScalarSampler doit également être utilisé pour les données de tableau. J'aurais juste besoin de changer la ligne

    samplers.put(SamplingType.Array, new ArraySampler());
    

    À

    samplers.put(SamplingType.Array, new ScalarSampler());
    

    Et cela fonctionnerait comme par magie. Je n'ai pas besoin de changer une seule ligne de code dans les classes appelantes, ce qui pourrait se compter par centaines. Effectivement, l'usine me permet de contrôler quoi et comment l'échantillonnage se produit, et tous les changements d'échantillonnage sont efficacement encapsulés dans un seul classe d'usine qui est interfacée avec le reste du système.

 50
Author: SWeko, 2014-01-01 06:06:09

Personnellement, j'utilise le modèle d'usine lorsque l'implémentation d'une interface est inconnue au moment de l'exécution ou qu'elle peut être rendue dynamique.

Cela signifie qu'en tant que développeur, je travaille contre une interface connue pour l'instance de l'objet, mais je ne suis pas préoccupé par le fonctionnement de l'implémentation.

Prenez, par exemple. Vous pouvez utiliser un modèle d'usine pour vous fournir des objets à partir d'une base de données. Vous ne vous souciez pas si cette base de données est un fichier plat, une base de données utilisateur locale/unique, un serveur base de données ou ressource web, seulement que l'usine peut générer et gérer ces objets.

Je détesterais avoir à écrire des implémentations pour chacun de ces cas: P

 4
Author: MadProgrammer, 2013-01-29 04:08:05

Du livre Java efficace de Joshua Bloch, partiellement réécrit par moi:

1) Les méthodes d'usine statiques (SFM), contrairement aux constructeurs, ont des noms.

public static ComplexNumber one () {
    return new ComplexNumber(1, 0);
}

public static ComplexNumber imgOne () {
    return new ComplexNumber(0, 1);
}

public static ComplexNumber zero () {
    return new ComplexNumber(0, 0);
}

2) Il n'est pas nécessaire de créer un nouvel objet à chaque fois SFM est/sont appelés

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE; 
}

3) SFM peut renvoyer un objet de n'importe quel sous-type de son type de retour.

4) SFM réduisez la verbosité de la création d'instances de type paramétrées.

public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}

Map<String, List<String>> m = HashMap.newInstance();
 4
Author: Nikolay Kuznetsov, 2017-09-26 19:56:23

Pour compléter la réponse de Thilo, supposons que vous ayez un objet qui n'a qu'un booléen en tant que constructeur: ce serait un gaspillage total d'en construire un à chaque fois, car il n'a que deux valeurs possibles.

Dans ce cas, vous pouvez créer des méthodes d'usine statiques. La classe Boolean de Java est un exemple: Boolean.valueOf().

 1
Author: fge, 2013-01-29 04:08:19

L'usine en elle-même ne montre pas sa beauté si facilement. Est-ce que lorsque vous le combinez avec d'autres modèles lorsque vous voyez les avantages réels par exemple, si vous souhaitez utiliser le motif décorateur, l'instanciation directe d'un objet peut ajouter un couplage supplémentaire à votre code. Comme le dit le professeur de POO, le couplage est mauvais :) donc si vous instanciez l'objet décoré et que vous ne voulez pas augmenter le couplage, vous pouvez utiliser une usine.

 0
Author: Juan Alberto López Cavallotti, 2013-01-29 04:09:22

Vous pouvez vous référer au wikipedia, mais l'idée de base de la plupart des modèles de conception est d'introduire une certaine abstraction pour obtenir une meilleure maintenabilité et/ou réutilisabilité. Le modèle de méthode d'usine ne fait pas exception, ce qu'il fait est d'abstraire la complexité de la création du code.

Pour un cas simple, il semble inutile d'utiliser un motif d'usine, un simple new suffit. Mais lorsque vous avez besoin de plus de flexibilité ou de fonctionnalité, ce modèle peut vous aider.

Par exemple, à moins qu'un une nouvelle instance est requise, la fabrique statique valueOf(boolean) est généralement un meilleur choix que new Bealean(boolean), car elle évite de créer des objets inutiles. Le modèle de méthode d'usine est également connu sous le nom de Virtual Constructor . Comme nous le savons, le polymorphisme est l'une des principales caractéristiques de la POO, mais le constructeur ne peut pas être polymorphe, cette lacune peut être surmontée par le modèle de méthode d'usine.

En substance, instancier un objet directement (généralement via new) est à peine une implémentation concrète , alors que le modèle de méthode d'usine protège un volatile mise en œuvre par un stable interface (non limitée au interface en Java), poussant la logique de création d'objets derrière une certaine abstraction pour assurer un code plus maintenable et réutilisable.

En dernier mot, pour bien comprendre les avantages du modèle de méthode d'usine et d'autres modèles de conception, il faut saisir l'essence de la POO, y comprisabstraction de données , abstraction polymorphe et SOLIDE principe.

 0
Author: Hui Zheng, 2013-01-29 06:01:53