2009-05-13 16 views
7

Disons que j'ai une boisson de classe abstraite, et une méthode d'usine qui choisit le type de boisson (vin, bière, etc.) à créer à l'exécution.Passer des arguments à une sous-classe spécifique, par une méthode d'usine

Chaque boisson a besoin d'arguments pour s'initialiser correctement. Certains d'entre eux sont communs à toutes les boissons; par exemple, ils peuvent tous avoir besoin d'un argument DrinkConfig.

Mais chaque boisson peut aussi avoir ses propres exigences. Peut-être que Wine a besoin d'un objet auxiliaire Sommelier pour s'initialiser. La bière n'a pas besoin de cela, mais elle peut avoir besoin de ses propres objets auxiliaires.

Alors, que dois-je passer à la méthode d'usine? Quand je l'appelle, j'ai tous les objets auxiliaires disponibles, alors je pourrais simplement les passer tous à l'usine. Mais cela pourrait aboutir à beaucoup d'arguments. Y a-t-il une meilleure façon de concevoir cela?

EDIT: Supposons que je ne peux pas simplement créer les objets auxiliaires en usine; ils sont uniquement disponibles auprès de l'appelant.

Répondre

4

Je créerais différentes méthodes de surcharge dans votre classe d'usine.

public class DrinkFactory { 

    public static Drink CreateBeer(DrinkConfig config, string hops) { 
     return new Beer(config, hops); 
    } 

    public static Drink CreateWine(DrinkConfig config, string grapes, int temperature) { 
     return new Wine(config, grapes, temperature); 
    } 
} 

Edit:

Si on désire pour avoir une seule méthode dans la classe d'usine serait une autre mise en oeuvre:

public enum DrinksEnum { 
    Beer, 
    Wine 
} 

public class DrinkFactory { 

    public static Drink CreateDrink(DrinksEnum drinkType, DrinkConfig config) { 
     switch(drinkType) { 
      case DrinksEnum.Beer: 
       return new Beer(config); 
      case DrinksEnum.Wine: 
       return new Wine(config); 
      default: 
       throw new ApplicationException("Drink type not recognised."); 
     } 
    } 
} 
+0

Les signatures sont correctes. Le problème est de savoir comment transmettre des arguments à votre Factory :: CreateDrink() (ou à tout autre nom). – dirkgently

+0

Si vous souhaitez avoir une méthode CreateDrink en usine, vous pouvez utiliser un paramètre enum pour spécifier quel type de boisson vous voulez. Je ne crois pas que l'approche ci-dessus soit conforme au modèle d'usine du GoF, où les objets sont créés dans la classe Drink (je dois consulter mon livre ce soir), mais je le trouve beaucoup plus pragmatique et conserve le principal avantage de la création centralisée d'objets pour les hiérarchies de sous-classe. – sipwiz

+0

Après avoir vérifié mon livre GoF Design Patterns, je suis heureux que l'exemple que j'ai donné ci-dessus montre à quel point vous utilisez une Factory dans le cadre du motif de conception d'Abstract Factory. Pour se conformer complètement, il devrait y avoir une classe d'usine abstraite dont DrinkFactory hérite, mais pour les cas simples comme celui-ci, je le laisse normalement de côté. Il serait assez facile de refactoriser DrinkFactory si une autre usine de béton était nécessaire. – sipwiz

0

Je suis tenté de fournir une solution naïve où votre les ingrédients sont dérivés d'une classe de base «DrinkIngredients». Vous devrez correspondre à la sous-classe à utiliser pour une boisson particulière.

Apparemment, vous pourriez être tenté de créer une autre usine pour les ingrédients - mais cela entraînerait un problème de poule et d'oeuf.

0

Généralement, une méthode d'usine existe pour masquer ces détails. Une question importante est d'où vient le sommelier - si tous ces autres assistants sont singleton ou peuvent être acquis à partir d'une source connue, instanciez alors l'usine les informations nécessaires pour aller les trouver, de sorte que votre code d'appel n'a pas besoin s'inquiéter à ce sujet.

De même, dans de nombreux cas, un framework tel que Spring serait utilisé pour vous permettre de décrire ces relations dans un fichier de configuration plutôt que dans du code.

Si vous avez vraiment besoin de passer les helpers à l'exécution à partir du code appelant, je vous suggère de lire l'article 'Arguments and Results' (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.50.7565) qui décrit un modèle commun pour rassembler des arguments complexes. Essentiellement, vous devez créer une collection intermédiaire des paramètres nécessaires et les transmettre à l'usine.

2

La méthode d'usine doit éliminer les détails de la création de valeurs. Vous ne devriez donc pas passer d'objets auxiliaires à la méthode usine, la méthode usine devrait créer l'objet auxiliaire dont elle a besoin et le transmettre au constructeur approprié.

0

Dans les cas comme celui-ci, je regarde généralement vers d'autres solutions au lieu de passer des variables.

Par exemple, dans votre cas - WineFactory avoir besoin d'un Sommelier, il peut donc construire le vin approprié -

Ceci est un cas d'utilisation pour l'injection de dépendance d'exécution. Un cadre d'injection de dépendance d'une certaine forme rendrait ce travail très simple, compréhensible et juste, sans avoir besoin de transmettre toutes ces propriétés.

1

Une usine doit créer des objets très similaires en premier lieu. Cela signifie que même si tous ces objets sont des boissons, la méthode d'usine peut ne pas être appropriée parce que chaque boisson est simplement très différente d'une autre. Cela étant dit, vous pouvez à la place passer une liste d'objets de taille égale au nombre de propriétés que vous souhaitez définir. Chaque objet représentera alors la valeur que vous voulez définir dans le constructeur de l'objet approprié, dans l'ordre dans lequel vous voulez définir ces variables. L'inconvénient est que vous devez formater une liste en dehors de l'usine avant de faire l'appel, ce qui est quelque peu maladroit.

0

Cela ressemble à un cas parfait pour le modèle Builder. Utilisez le modèle Factory pour créer des objets similaires et le modèle Builder pour construire des objets complexes et dissemblables. Si vous tentez d'utiliser le modèle Factory pour ce problème, plusieurs constructeurs d'initialisation dissemblables (avec différents nombres/types de paramètres) seront utilisés pour les différents objets.