2009-05-07 14 views
5

He estado revisando algún código que un colega mío está usando 'clases de marcador' para controlar la lógica del programa (consulte el ejemplo artificial a continuación). Parece que funciona bien, y el código lee muy bien, pero hay algo acerca de lo que huele ...Uso de clases de marcador para controlar el flujo lógico

namespace ConsoleApplication4983 
{ 
    public class MyClass 
    { 
     static void Main() 
     { 
      var c = new MyClass(); 
      c.DoSomething(new Sequential()); 
      c.DoSomething(new Random()); 
     } 

     public void DoSomething(ProcessingMethod method) 
     { 
      if (method is Sequential) 
      { 
       // do something sequential 
      } 
      else if (method is Random) 
      { 
       // do something random 
      } 
     } 
    } 

    public class ProcessingMethod {} 
    public class Sequential : ProcessingMethod {} 
    public class Random : ProcessingMethod {} 
} 

lo que sería una mejor forma de lograr el mismo efecto? ¿Enumerados? Atributos?

Respuesta

8

Marker interfaces de son una mejor práctica, ya que ofrecen mucha más flexibilidad.

Sin embargo, en este caso específico, creo que el envío virtual es una mejor solución.

using System; 

namespace ConsoleApplication4983 
{ 
    public class MyClass 
    { 
     static void Main() 
     { 
      var c = new MyClass(); 
      c.DoSomething(new Sequential()); 
      c.DoSomething(new Random()); 
     } 

     public void DoSomething(ProcessingMethod method) 
     { 
      method.Foo(); 
     } 
    } 

    public class ProcessingMethod 
    { 
     public virtual void Foo() { } 
    } 
    public class Sequential : ProcessingMethod 
    { 
     public override void Foo() { } 
    } 
    public class Random : ProcessingMethod 
    { 
     public override void Foo() { } 
    } 
} 
1

Estaba casi allí, pero no del todo, y eso es probablemente lo que está viendo. La declaración if del tipo es el mal olor. El hacer algo debería haber estado en la clase base ProcessingMethod y cada tipo que lo extendió debería tener su propia versión.

public void DoSomething(ProcessingMethod method) { 
    method.DoSomething(); 
} 
0

¿Qué le parece delegar la lógica de procesamiento a la subclase específica? ProcessingMethod tendrían algún método abstracto implementado por cada subclase.

public void DoSomething(ProcessingMethod method) 
{ 
    method.Process(); 
} 

public abstract class ProcessingMethod 
{ 
    public abstract void Process(); 
} 

public class Sequental : ProcessingMethod 
{ 
    public override void Process() 
    { 
    // do something sequential 
    } 
} 

public class Random : ProcessingMethod 
{ 
    public override void Process() 
    { 
    // do something random 
    } 
} 
0

Sí, esto huele mal. Si desea hacer algo en paralelo:

public class Parallel : ProcessingMethod{} 

, entonces tendrá que cambiar una gran cantidad de código.

4

Lo que desea hacer es reemplazar esto con un strategy pattern. Una estrategia define cómo se hace algo, es decir, un algoritmo.

public interface IProcessingMethod 
{ 
    void Process(); 
} 

public class SequentialProcess : IProcessingMethod 
{ 
    public void Process(IProcessable obj) 
    { 
     do something sequentially with the obj 
    } 
} 

public class ParallelProcess : IProcessingMethod 
{ 
    public void Process(IProcessable obj) 
    { 
     do something in parallel with the obj 
    } 
} 

public interface IProcessable 
{ 
    void Process(IProcessingMethod method); 
} 

public class MyClass : IProcessable 
{ 
    public void Process(IProcessingMethod method) 
    { 
     method.Process(this); 
    } 
} 

... 

var obj = new MyClass(); 
obj.Process(new SequentialProcess()); 

Ahora bien, si tengo un nuevo tipo de ProcessingMethod, que sólo tendrá que crear la clase para ese método y cambiar el código que determina lo que se inyecta método de procesamiento con el método de proceso de mi objeto IProcessable.

+0

+1 Este es un buen enfoque. –

0

Directrices de diseño del marco libro recomienda contra el uso de interfaces de marcador (y presumiblemente clases de marcador), prefiriendo los atributos intead. Habiendo dicho eso, el libro continúa diciendo que usar is (como lo has hecho) es mucho más rápido que utilizar la reflexión para verificar un atributo.

2

Veo que esta pregunta es antigua, pero creo que todas las respuestas no fueron entendidas.

Si el ejemplo ilustra completamente el alcance de la funcionalidad requerida, entonces la construcción apropiada para usar aquí sería Enum tipo. Los tipos Enum son tipos de valores; funcionan esencialmente como constantes numéricas nombradas, con gran compatibilidad con autocompletar IDE. Aquí está el ejemplo modificado para utilizar un tipo de enumeración:

namespace ConsoleApplication4983 
{ 
    public class MyClass 
    { 
     static void Main() 
     { 
      var c = new MyClass(); 
      c.DoSomething(ProcessingMethod.Sequential); 
      c.DoSomething(ProcessingMethod.Random); 
     } 

     public void DoSomething(ProcessingMethod method) 
     { 
      if (method == ProcessingMethod.Sequential) 
      { 
       // do something sequential 
      } 
      else if (method == ProcessingMethod.Random) 
      { 
       // do something random 
      } 
     } 
    } 

    public enum ProcessingMethod 
    { 
     Sequential, 
     Random 
    } 
} 

Las otras respuestas están haciendo referencia a los patrones más elaborados. Creo que leen demasiado en el término "clase de marcador". A veces, el patrón de estrategia, el despacho virtual, etc. son un buen camino a seguir, pero en este caso creo que un Enum es la mejora más simple que se puede hacer con este código.

Cuestiones relacionadas