2010-02-25 11 views
11

Parece que estoy mentalmente atrapado en un dilema de patrón Flyweight.Flyweight y problema de fábrica con IDisposable

En primer lugar, vamos a decir que tengo un tipo desechable DisposableFiddle y una fábrica FiddleFactory:

public interface DisposableFiddle : IDisposable 
{ 
    // Implements IDisposable 
} 

public class FiddleFactory 
{ 
    public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s) 
    { 
     // returns a newly created fiddle. 
    } 
} 

Luego, en mi opinión, es bastante claro para el cliente de FiddleFactory que la fábrica renuncia a la propiedad del violín creado y que es responsabilidad del cliente deshacerse del violín cuando haya terminado con él.

Sin embargo, vamos a su lugar decir que quiero compartir violines entre los clientes mediante el uso de la flyweight:

public class FiddleFactory 
{ 
    private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;   

    public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s) 
    { 
     // returns an existing fiddle if a corresponding s is found, 
     // or a newly created fiddle, after adding it to the dictionary, 
     // if no corresponding s is found. 
    } 
} 

entonces me siento moralmente obligado a hacer la propia fábrica desechable, ya que crea los violines y mantiene las referencias a ellos durante toda su vida. Pero eso causaría problemas a los clientes que asumieron que poseían los violines y, por lo tanto, deberían eliminarlos.

es el problema en realidad que llamo la fábrica FiddleFactory en lugar de, digamos, FiddlePool, y el método de la "creación" en lugar de CreateFiddleGetFiddle? De esta manera:

public class FiddlePool : IDisposable 
{ 
    private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;   

    public DisposableFiddle GetFiddle(SomethingThatDifferentiatesFiddles s) 
    { 
     // returns an existing fiddle if a corresponding s is found, 
     // or a newly created fiddle, after adding it to the dictionary, 
     // if no corresponding s is found. 
    } 

    // Implements IDisposable 
} 

Entonces es más claro para el cliente que no va a poseer el violín vuelto y es la responsabilidad de la piscina para disponer los violines.

¿O esto solo se puede resolver fácilmente documentando?

¿Hay alguna forma de salir del dilema? ¿Hay incluso un dilema? :-)

Respuesta

7

puedo ver dos formas de salir de este problema:

  • ThreadPool de estilo: rediseñar las clases por lo que el FiddlePool proporciona una interfaz para hacer las cosas más incómoda. El grupo no reparte instancias Fiddle porque tiene un método FiddlePool.PlayFiddle en su lugar. Como el grupo controla los tiempos de vida del violín, es responsable de deshacerse de ellos.

  • SqlConnection de estilo: modificar método dispose pública Fiddle 's para que realmente sólo devuelve a la piscina violines violín (que encapsula la clase de violín). Internamente, el grupo de violines se ocupa de realmente liberando recursos desechables.

+0

Gracias, el primero sigue mejor la Ley de Demeter y el segundo se ajusta mejor en mi diseño general. Hmmm ... –

+0

Pondré mis dos centavos por el primer acercamiento. ¡He visto a demasiadas personas tratar de escribir su propio grupo de SqlConnnection! (En realidad no, pero * he * tenido que explicar por qué no es necesario.) –

2

Estoy de acuerdo con su segunda opinión. La terminología 'Pool' y 'Get' hacen las cosas más claras para el consumidor. Sin embargo, todavía no aclara lo suficiente, y siempre se debe agregar documentación para garantizar una comprensión completa y válida.

+3

Mis pensamientos exactamente. Además, probablemente no llame a su clase DisposableFiddle a menos que desee que el usuario la elimine() ... –

+0

@Reed: Estoy de acuerdo con DisposableFiddle. El nombramiento correcto es fundamental para transmitir el intento, y es la primera línea de defensa contra el uso incorrecto. – jrista

+0

Heh, sí, acabo de colocar Desechable en el nombre del ejemplo para que sea más fácil seguirlo en mis (confusos) pasos ... –

1

Debe hacer algo más que solo documentación y métodos de denominación para indicar a los clientes que no llamen a deshacerse de ellos. En realidad, sería mejor hacer que los clientes llamen a disponer, así que haga ese patrón de uso. Tome alguna dirección de nuestra base de datos. Se crean grupos de conexiones.

La base de datos reúne un conjunto de conexiones que, por sí solas, son compatibles con la agrupación.El código de llamada crea una conexión, la abre y las llamadas se cierran (eliminan) en ella. El código de llamada ni siquiera sabe si está agrupado o no, todo lo maneja internamente la clase de conexión. Con una conexión agrupada, la llamada a Open() se ignora si la conexión ya está abierta. Se llama a Close()/Dispose() y, en el caso de una conexión agrupada, en realidad devuelve la conexión al grupo en lugar de cerrarla.

Puede hacer lo mismo creando una clase PooledFiddle que anule Dispose y devuelva el objeto al grupo. Lo ideal es que el cliente ni siquiera tenga que saber que es un Fiddle agrupado.

+0

"Idealmente, el cliente ni siquiera tendría que saber que se trata de un Fiddle agrupado". - Sí, estoy de acuerdo en eso. Creo que es un detalle de implementación que está mejor oculto. –

Cuestiones relacionadas