2009-03-05 23 views
7

Una de las formas de implementar la inyección de dependencias correctamente es separar la creación de objetos de la lógica comercial. Típicamente, esto implica el uso de una fábrica para la creación de objetos.¿Está bien pasar parámetros a un método de Fábrica?

Hasta este punto, nunca he considerado seriamente el uso de una fábrica así que pido disculpas si esta pregunta parece un poco simplista:

En todos los ejemplos del patrón de la fábrica que he correr a través, siempre ver ejemplos muy simples que no tienen parametrización. Por ejemplo, aquí hay una fábrica robada de Misko Hevery's excelente artículo How To Think About the "new" Operator.

 
class ApplicationBuilder { 
    House build() { 
    return new House(new Kitchen(
       new Sink(), 
       new Dishwasher(), 
       new Refrigerator()) 
      ); 
    } 
} 

Sin embargo, ¿qué ocurre si quiero que cada casa que construyo a tener un nombre? ¿Todavía utilizo el patrón Factory si reescribo este código de la siguiente manera?

 
class ApplicationBuilder { 
    House build(const std::string & house_name) { 
    return new House(house_name, 
         new Kitchen(new Sink(), 
            new Dishwasher(), 
            new Refrigerator()) 
        ); 
    } 
} 

Nota que mi llamada al método de fábrica ha pasado de esto:

 
ApplicationBuilder builder; 
House * my_house = builder.build();

A esto:

 
ApplicationBuilder builder; 
House * my_house = builder.build("Michaels-Treehouse");

Por cierto: creo que el concepto de separar objeto crear instancias de lógica de negocios es genial, solo intento descubrir cómo puedo aplicarlo a mi propia situación. Lo que me confunde es que todos los ejemplos que he visto del patrón Factory nunca pasan ningún parámetro a la función build().

Para ser claros: no sé el nombre de la casa hasta el momento antes de que necesite crear una instancia.

+0

A menudo he visto fábricas tomar parámetros como lo ha demostrado. No tiene nada de malo. O en términos de tu pregunta. Está perfectamente bien. – grieve

Respuesta

8

He visto un montón de ejemplos que usan un conjunto fijo de argumentos, como en su nombre, y también los he usado y no veo nada de malo en ello.

Sin embargo hay una buena razón por la que muchos tutoriales o artículos pequeños deben evitar mostrar fábricas que reenvían los parámetros a los objetos construidos: Es prácticamente imposible reenviar número arbitrario de argumentos (aunque sea por un límite de cuerdo como 6 argumentos). Cada parámetro que envíe se debe aceptar como const T& y T& si desea hacerlo genérico.

Para ejemplos más complicados, sin embargo, necesita un conjunto de sobrecargas exponencialmente creciente (para cada parámetro, una const y una versión sin contraste) y perfect forwarding no es posible (de modo que los temporales se reenvían como temporales, por ejemplo) . Para el próximo C++ estándar esa cuestión se resuelve:

class ApplicationBuilder { 
    template<typename... T> 
    House *build(T&&... t) { 
    return new House(std::forward<T>(t)..., 
         new Kitchen(new Sink(), 
            new Dishwasher(), 
            new Refrigerator()) 
        ); 
    } 
}; 

De esta manera, se puede llamar

builder.build("Hello", 13); 

y volverá

new House("Hello", 13, new Kitchen(new Sink(... 

leer el artículo he vinculado anteriormente.

+0

¡Buen enlace sobre las referencias de rvalue! Probablemente esté más allá del alcance de la pregunta, pero es uno de los artículos más claros que he leído sobre el tema. –

5

No veo por qué sería incorrecto agregar este parámetro a su fábrica. Pero tenga en cuenta que no debe agregar muchos parámetros que podrían no ser útiles para todos los objetos creados por la fábrica. ¡Si lo haces, habrás perdido bastantes ventajas de una fábrica!

+0

Al describir qué tipo de objeto desea (en los parámetros) esto puede permitirle a la fábrica devolver objetos completamente diferentes (devolviendo una clase base o una interfaz). – Aardvark

1

Estoy de acuerdo con Benoit. Sin embargo, piense en una fábrica para crear algo así como conexiones sql, en un caso como este sería necesario pasar información sobre la conexión a la fábrica. La fábrica usará esa información para usar el protocolo de servidor correcto, etc.

4

La idea de una fábrica es que le da una instancia de una clase/interfaz, por lo que no hay nada de malo en pasar los parámetros. Si lo hubiera, sería malo pasar los parámetros a un nuevo() también.

5

No solo es aceptable, sino que es común para pasar los parámetros a un método de fábrica. Consulte some examples. Normalmente, el parámetro es un tipo que le dice a la fábrica qué hacer, pero no hay ninguna razón para que no pueda agregar otra información que necesita para construir un objeto. Creo que lo que estás haciendo está bien.

1

Claro, ¿por qué no ...!?

Lo bueno de pasar parámetros es que le permite ocultar la implementación del objeto concreto. Por ejemplo, en el código que publicas, pasas los parámetros al constructor. Sin embargo, puede cambiar la implementación para que se aprueben mediante el método Initiailze. Al pasar los parámetros al método de fábrica, oculta la naturaleza de construir e inicializar el objeto de la persona que llama.

1

Eche un vistazo a Loki :: Factory, pero hay una implementación muy similar a la de Boost. Un código de ejemplo que utilizo regularmente en diferentes sabores:

typedef Loki :: SingletonHolder < Loki :: fábrica de componentes <, std :: string, Loki :: lista de Tipos < const DataCollection &, Loki :: lista de Tipos < juego *, Loki :: NullType>>>> ComponentFactory;

Esto puede parecer un poco raro a primera vista, sin embargo déjame explicarte esta bestia y qué tan poderosa es realmente. Básicamente creamos un singleton que contiene una fábrica, la mayoría de los parámetros son para singleton, Component es nuestro producto, std :: string es nuestro tipo de id de creación, después de esto sigue una lista de tipos de params que es necesaria para la creación de componentes (Esto también se puede definir usando una macro para una sintaxis menos detallada). Después de esta línea uno puede hacer:

ComponentFactory :: Instance(). CreateObject ("someStringAssociatedWithConcreteType", anDataCollection, aGamePointer);

Para crear objetos, para registrar uno simplemente use ComponentFactory :: Instance(). Register() ;. Hay un gran capítulo sobre los detalles en el libro Modern C++ Design.

Cuestiones relacionadas