2011-03-30 11 views
7

Estoy tratando de diseñar un conjunto de clases de fábrica para nuestro sistema, donde algunos objetos creados por la fábrica también deben inicializarse antes de que puedan ser utilizados correctamente.Clase de fábrica con inicialización de objetos - tratando de evitar estática

Ejemplo:

$foobar = new Foobar(); 
$foobar->init($qux, ...); 
// $foobar ready for usage 

Por la misma de ejemplo, digamos que el objeto $qux es la única dependencia que Foobar necesidades. Lo que me gustaría llegar a es:

$foobar = Foo_Factory('bar'); 

Con el fin de evitar la necesidad de pasar a lo largo del objeto $qux en todo el sistema y pasarlo a la clase de fábrica como un parámetro más, me gustaría llevar a cabo inicialización de Foobar directamente en la clase de fábrica:

class Foo_Factory { 

    public static function getFoo($type) { 

     // some processing here 
     $foo_name = 'Foo' . $type; 
     $foo = new $foo_name(); 
     $foo->init($qux); 

     return $foo; 
    } 

} 

Hay pocas soluciones que vienen a la mente, pero ninguno de ellos es ideal:

  1. Añadir un colocador estática metanfetamina od para $qux a la clase de fábrica, y que almacene una referencia a $qux en una variable privada estática. El sistema puede establecer $qux al inicio, y la clase de fábrica puede evitar cualquier cambio futuro (por razones de seguridad).
    Aunque este enfoque funciona, el uso de un parámetro estático para almacenar la referencia a $qux es problemático durante las pruebas unitarias (por ejemplo, afortunadamente la vida sobrevive entre las pruebas individuales debido a su estado estático).
  2. Crea una nueva clase de contexto usando el patrón Singleton y deja que la clase de fábrica lo use para obtener una referencia a $qux. Esta podría ser una forma un poco más limpia de hacer esto que la opción n. ° 1 (aunque movemos el problema estático de la clase de fábrica a la clase de contexto).
  3. Use la inyección de dependencia por completo, es decir, pase $qux a cualquier objeto que use la clase de fábrica, y deje que ese objeto lo transmita a la clase de fábrica como otro parámetro: Foo_Factory::getFoo($type, $qux);.
  4. Igual que el anterior (n. ° 3), pero en lugar de pasar $qux a lo largo del sistema, pase una instancia de la clase de fábrica (es decir, en este caso no sería estático, pero sería instantilable).

¿Qué recomendarías por favor? ¿Alguna de las cuatro alternativas mencionadas anteriormente, o hay una mejor manera de hacerlo, por favor?

Nota: No quiero entrar en un llamarada static is evil aquí, solo tratando de encontrar la mejor solución.

Respuesta

5

I'd ir con Dependency Injection todo el camino. Pero, en lugar de pasar $ qux por todas partes, simplemente regístrelo en el Contenedor de Inyectores de Dependencia y deje que el contenedor lo resuelva. En Symfony Component habla:

// Create DI container 
$container = new sfServiceContainerBuilder(); 

// Register Qux 
$container->setService('qux', $qux); 
// Or, to have the DI instanciate it 
// $container->register('qux', 'QuxClass'); 

// Register Foobar 
$container->register('foobar', 'Foobar') 
      ->addArgument(new sfServiceReference('qux')); 

// Alternative method, using the current init($qux) method 
// Look! No factory required! 
$container->register('altFoobar', 'Foobar') 
      ->addMethodCall('init', array(new sfServiceReference('qux'))); 
+0

Ah, eso es muy bueno en realidad - veo que voy a tener que mirar en sinfonía con más detalle (prácticamente) cuando tengo la oportunidad. Creé una solución de contenedor simple, y mientras sigo usando las fábricas internamente, funciona como un encanto :-) ¡Gracias por los comentarios y el ejemplo! – MicE

4

yo solo hago los métodos de fábrica no estática y pasarlo a todos los objetos que necesitan esa fábrica.

Para configurar la fábrica necesitarás alimentarlo con tu parámetro $qux en el constructor.

class Foo_Factory { 

    public function __construct($qux) { 
     $this->qux = $qux; 
    } 

    public function getFoo($type) { 
     // some processing here 
     $foo_name = 'Foo' . $type; 
     $foo = new $foo_name(); 
     $foo->init($this->qux); 

     return $foo; 
    } 
} 

Con ese enfoque que debe obtener la facilidad de uso en las clases en las que necesita para trabajar con la fábrica sin el "problema" de pasar alrededor de un contenedor de servicio o un registro.

Para este ejemplo utilizaría el enfoque directo de simplemente pasar alrededor de los objetos que realmente necesita y no resumirlos en clases de Contenedor.

La decisión de utilizar un DIC o un Registro o una antigua DI es algo que, creo, debería hacerse para todo el proyecto. Yo prefiero un DIC sobre un Registro, pero como DI normal, aún mejor. Para el contexto dado, es difícil argumentar a favor o en contra de un cierto enfoque.

Para resumir: si la fábrica estática es el problema simplemente hazla no estática.

esperanza he entendido su poste derecho;)

+0

Gracias edorian. Al final utilicé un DIC (porque en realidad tengo más dependencias que '$ qux'), y lo implementé en todo el sistema. Eso lo limpió significativamente por sí solo, sin mencionar las mejoras en la capacidad de prueba de la unidad :-) – MicE

Cuestiones relacionadas