2010-03-17 31 views
6
public class StepClause 
{ 
    public NamedStepClause Action1() {} 

    public NamedStepClause Action2() {} 
} 

public class NamedStepClause : StepClause 
{ 
    public StepClause Step(string name) {} 
} 

Básicamente, quiero ser capaz de hacer algo como esto:diseño de la interfaz y el código Fluido huelen

var workflow = new Workflow().Configure() 
    .Action1() 
    .Step("abc").Action2() 
    .Action2() 
    .Step("def").Action1(); 

Así, algunos "pasos" se nombran y algunos no lo son.

Lo que no me gusta es que StepClause tenga conocimiento de su clase derivada NamedStepClause.

Intenté un par de cosas para hacer que esto sienta mejor conmigo. Traté de mover las cosas a las interfaces, pero luego el problema simplemente se trasladó del hormigón a las interfaces: INamedStepClause aún debe derivarse de IStepClause e IStepClause necesita devolver INamedStepClause para poder llamar a Step(). También podría hacer que Step() forme parte de un tipo completamente separado. Entonces no tienen este problema y nos gustaría tener:

var workflow = new Workflow().Configure() 
    .Step().Action1() 
    .Step("abc").Action2() 
    .Step().Action2() 
    .Step("def").Action1(); 

que está bien, pero me gustaría hacer el paso a nombrar opcional si es posible.

Encontré esta otra publicación en SO here que parece interesante y prometedora. ¿Cuáles son sus opiniones? Creo que la solución original es completamente inaceptable o no?

Por cierto, esos métodos de acción tomarán predicados y funtores y no creo que quiera tomar un parámetro adicional para nombrar el paso allí.

El objetivo de todo esto es, para mí, definir solo estos métodos de acción en un solo lugar y en un solo lugar. Entonces, las soluciones del enlace al que se hace referencia utilizando genéricos y métodos de extensión parecen ser los mejores enfoques hasta ahora.

+0

Simplemente no me gusta la mayoría de las respuestas que recibo. La otra razón es que lo olvido; P –

Respuesta

9

Le daré dos opciones.

Opción A

var a = new A.NamedStepClause(); 

a.Action1() 
    .Step("abc").Action2() 
    .Action2() 
    .Step("def").Action1(); 


namespace A 
{ 
    public class StepClause<SC> where SC : StepClause<SC> 
    { 
     public SC Action1() { return null; } 
     public SC Action2() { return null; } 
    } 

    public class NamedStepClause : StepClause<NamedStepClause> 
    { 
     public NamedStepClause Step(string name) { return null; } 
    } 
} 

Opción B

var b = new B.StepClause(); 

b.Action1() 
    .Step("abc").Action2() 
    .Action2() 
    .Step("def").Action1(); 

namespace B 
{ 
    public class StepClause 
    { 
     public StepClause Action1() { return null; } 
     public StepClause Action2() { return null; } 
    } 

    public static class StepClauseExtensions 
    { 
     public static StepClause Step(this StepClause @this, string name) 
     { return null; } 
    } 
} 

Ambas opciones de compilación y le dan la interfaz fluida que usted está buscando. Estoy más inclinado a ir con la opción A, ya que te da acceso al funcionamiento interno de la clase. El uso de métodos de extensión significa que puede necesitar dar algún tipo de acceso externo a su clase, rompiendo así la encapsulación.

¡Buena suerte!

+0

+1 para la opción A –

+1

Tomaré la opción A con un cambio en el método Step en NamedStepClause; debería devolver StepClause en lugar de NamedStepClause; de lo contrario, me meto en una situación en la que puede hacer: Paso ("1"). Paso ("2"). Paso ("3") ... Tal vez ese requisito no estaba claro - lo siento –

+0

La opción B no realmente trabajo por la misma razón que indiqué en el comentario anterior. –

Cuestiones relacionadas