2012-08-31 10 views
5

He creado un patrón de estilo de constructor fluido para ayudar con los datos que cargan mis pruebas. El orden de ciertos métodos es importante y se preguntaba cuál es el método preferido para gestionar la secuencia correcta.¿Cuál es la forma preferida de administrar el pedido en el patrón del generador?

Tengo el siguiente en la actualidad

using NUnit.Framework; 

[TestFixture] 
public class DataBuilderTests 
{ 
    [Test] 
    public void Can_NAME() 
    { 
     new DataLoader() 
      .Start() // must be called first 
      .Setup() // then called next 
      .LoadEmployees() // optional order not NB 
      .LoadProducts() // optional order not NB 
      .StartCleanup() // begin cleanup 
      .CleanupEmployees() // optional order not NB 
      .CleanupProducts() // optional order not NB 
      .End(); 
    } 
} 

public class DataLoader 
{ 
    public DataBuilderSetup Start() 
    { 
     return new DataBuilderSetup(this);  
    } 
} 

public class DataBuilderSetup 
{ 
    private readonly DataLoader _dataLoader; 

    public DataBuilderSetup(DataLoader dataLoader) 
    { 
     _dataLoader = dataLoader; 
    } 

    public DataBuilderOptions Setup() 
    { 
     // do setup 
     return new DataBuilderOptions(_dataLoader); 
    } 
} 

public class DataBuilderOptions 
{ 
    private readonly DataLoader _dataLoader; 

    public DataBuilderOptions(DataLoader dataLoader) 
    { 
     _dataLoader = dataLoader; 
    } 

    public DataBuilderOptions LoadEmployees() 
    { 
     // load 
     return this; 
    } 

    public DataBuilderOptions LoadProducts() 
    { 
     // load 
     return this; 
    } 

    public DataBuilderCleanupOptions StartCleanup() 
    { 
     return new DataBuilderCleanupOptions(_dataLoader); 
    } 
} 

public class DataBuilderCleanupOptions 
{ 
    private readonly DataLoader _dataLoader; 

    public DataBuilderCleanupOptions(DataLoader dataLoader) 
    { 
     _dataLoader = dataLoader; 
    } 

    public DataBuilderCleanupOptions CleanupEmployees() 
    { 
     // cleanup 
     return this; 
    } 

    public DataBuilderCleanupOptions CleanupProducts() 
    { 
     // cleanup 
     return this; 
    } 

    public DataLoader End() 
    { 
     return _dataLoader; 
    } 
} 
+0

¿Qué pasa con su solución actual? –

+0

Nada, solo algo que se me ocurrió esta mañana y tenía curiosidad sobre cómo otros manejaban el escenario – Chev

+0

¿Se debe llamar a 'CleanupEmployees' si se llama a' LoadEmployees'? –

Respuesta

0

La forma preferida es evitar a toda costa. Diseña tu generador así que es obvio lo que se necesita hacer.

ObjectBuilder 
.Init() 
.Optional1() 
.Optional3() 
.Optional2() 
.ToObject() 

Si algo tiene que pasar primero, haga esto en el constructor o en el método de fábrica. Luego, siempre tenga un método que complete el proceso de compilación, la limpieza y todo.

+0

Lo hago en este momento, pero imagine un escenario en el que una elección solo puede existir si se utiliza una selección previa. El escenario típico es una cláusula OrderBy común que tiene una opción ASC o DESC que procede. Estas opciones solo existirían si se selecciona OrderBy. – Chev

+0

@ Chev - En realidad, es un escenario muy diferente. Lo que describes realmente encadena constructores. En ese caso, no podría agregar un nuevo predicado where porque está tratando con un nuevo constructor. El camino de la construcción sería inequívoco. – ChaosPandion

1

Puede agregar un miembro de cola privada al generador, por ejemplo Queue<string> y agregar nombres de la operación en cada paso del generador.

.Build() método o en su caso .End() comprobará que la cola contenga los nombres correctos de la operación en el orden correcto. Si no, puede lanzar un InvalidOperationException

También puede usar un árbol como una estructura de datos. Un árbol le permitirá analizar opciones que no están disponibles dados los pasos previos del constructor.

Pero mejor considerar otras respuestas ya que mi enfoque es en realidad un truco y creará un problema de mantenimiento.

2

Una parte de las fortalezas del BuilderPattern es que puede proteger a los consumidores del pedido "mágico" impuesto de llamadas a métodos.

recomendaría cambiar su diseño para que sea:

  • Todos los argumentos necesarios se proporcionan en la delantera para proteger a los consumidores de la forzada ordenó llamadas a Start y Setup.
  • Modifique las responsabilidades de sus entidades para que puedan crearse arbitrariamente.

Obviamente esto es my preferencia personal. Si este tipo forma parte de una biblioteca que será consumida por terceros, le recomiendo que elimine la necesidad de ordenar el método mágico. Si esto solo es probable que se use en la casa, depende de usted sopesar los costos asociados con el cambio de código y no hacerlo.

+0

Curiosamente, lo que comenzó mi pensamiento fueron libs de terceros como FluentNHibernate, FluentValidation y NHibernate que parecen hacer esto. – Chev

+0

De acuerdo, se frustra uno de los principales propósitos de un constructor al tener que llamar a los métodos en cierto orden solo para configurarlo. El constructor debe almacenar en caché las partes y luego ordenarlas en la llamada final de compilación. – tcarvin

0

En Java (C# y su herencia múltiple no debe hacer que sea diferente) que podría hacerlo de esa manera:

conjunto declarar de interfaces, que contiene un solo método:

Interface DoFirstThing { // could be renamed to "BuilderOnStart" or "BuilderStartingState" 
    DoSecondThing doFirst(); 
} 

Interface DoSecondThing { 
    DoLastThing doSecond(); 
} 

Interface DoLastThing { 
    BuilderReady doLast(); 
} 

Interface BuilderReady { 
    Result build(); 
} 

class BuilderWithForcedSequence implements DoFirstThing, DoSecondThing, DoLastThing, BuilderReady { 

    // implement all 

} 

y finalmente lo que se necesita algún método de fábrica o de la fábrica para establecer el estado inicial para que el formador de:

public DoFirstThing createNewBuilderWithForcedSequence(requiredParameters){ 
    return new BuilderWithForcedSequence(requiredParameters); 
} 

Esto produciría constructor, la solicitud forzada de la construcción de la metanfetamina ods (se les debe cambiar el nombre de doThat a algo significativo), que solo pueden llamar a doFirst(), después de eso doSecond() ... y así sucesivamente, hasta el estado final, cuando se construiría obiect, usando el método build().

Result result = builder.doFirst().doSecond().doLast().build(); 


EDIT:
Vaya, eso es bastante exacta de su enfoque :)

0

Su solución actual es el enfoque que tomaría para proporcionar una sintaxis fluidez, aunque no necesariamente decir exactamente sigue el patrón del constructor Básicamente, lo que está haciendo es encadenar constructores junto con restricciones proporcionadas por una máquina de estado. Veo muy poca diferencia en lo que está haciendo que con cualquier otra configuración fluida comúnmente aceptada, como la fluencia fluida o las afirmaciones fluidas.

Cuestiones relacionadas