2009-07-28 9 views
12

Quiero devolver una interfaz y dentro de una declaración de cambio me gustaría configurarlo. ¿Es este un mal diseño?¿Es aplicable una declaración de interruptor en un método de fábrica? C#

private IResultEntity GetEntity(char? someType) 
    { 
     IResultEntity entity = null; 

     switch (someType) 
     { 
      case 'L': //life 
       entity = new LifeEntity(); 
       break; 
      case 'P': //property 
       entity = new PropertyEntity(); 
       break; 
      case 'D': //disability 
       entity = new DisabilityEntity(); 
       break; 
      case 'C': //credit card 
       entity = new CreditCardEntity(); 
       break; 
     } 

     return entity; 
    } 

Respuesta

19

Normalmente no me importa cambiar las instrucciones en una fábrica, siempre que pueda agrupar y controlar todas las clases derivadas que quiero que mi fábrica cree con anticipación.

A veces, tal vez un complemento creado por el usuario podría querer agregar sus propias clases a esa lista de conmutadores, y luego una instrucción swich no es suficiente.

me encontré con este buen source for some more info en la creación de algunas clases de fábrica/versátiles más potentes

Una buena solución híbrida normalmente tomar es mantener un diccionario estático < cadena, tipo> para cada clase de fábrica.

La gente puede "registrar" sus propias implementaciones utilizando algún tipo de

Factories.TypeRegistration.StaticDictionary.Add("somekey",typeof(MyDerivedClass)) 

(o mejor aún, utilizar un método de registro y ocultar el StaticDictionary)

entonces la fábrica tiene una tarea fácil de crear una instancia realizando una búsqueda en la tabla:

Activator.CreateInstance(Factories.TypeRegistration.StaticDictionary["somekey"]); 
+0

Así es como normalmente me acerco a una fábrica, especialmente en bibliotecas donde las aplicaciones cliente pueden querer agregar sus propias implementaciones. – Joon

+0

Esa fuente para obtener más enlace de información ya no está disponible, ¿hay alguna posibilidad de que se aloje en otro lugar? ¿O hay una publicación equivalente en alguna parte? –

+0

@Sam Heuck He vinculado una versión archivada – Lennart

1

No diría que es un mal diseño, aunque es potencialmente bastante rígido. La única forma de extender esto sería a través de la recompilación.

3

No sé qué posibilidades tienes en C#, pero aún es mejor tener un interruptor en un método de fábrica que tener interruptores por todos lados. En un método de fábrica, un interruptor es tolerable, pero es mejor que esté bien documentado.

+5

Esta respuesta resume casi todo lo que iba a decir. Yo agregaría que lo único que cambiaría en la implementación de su fábrica es que usaría una enumeración en lugar de un carácter de nulo para ser la clave de fábrica. – wtaniguchi

+0

¿Cómo convierto un carácter que se puede anular a una enumeración? No pensé que pudieras hacer una enumeración de cadenas o caracteres. Pensé que representan valores numéricos. – Hcabnettek

+1

Definitivamente de acuerdo con la enumeración sobre el carácter "mágico". Además, es posible que prefiera lanzar una excepción si no puede encontrar el estuche correcto en lugar de devolver nulo. Creo que podría ser solo una preferencia. – kevindaub

2

No creo que haya nada de malo en esto. Sí, las declaraciones de cambio son un olor a código, pero en mi libro, están bien en este tipo de situación. En realidad, hay poco más que podrías hacer para lograr cosas como esta.

3

Prefiero tener el tipo que desea crear una instancia para un valor específico en un archivo de configuración. algo como:

<TypeMappings>
< nombre TypeMapping = tipo "vida" = "Entities.LifeEntity, Entidades"/>
< TypeMapping name = tipo "propiedad" = "Entities.PropertyEntity, Entidades"/>
< nombre TypeMapping = tipo "discapacidad" = "Entities.DisabilityEntity, Entidades"/>
< TypeMapping name = tipo "tarjeta de crédito" = "Entities.CreditCardEntity, Entidades"/>
</TypeMappings >

Dentro de su método que a continuación podría extraer todos los registros del archivo de configuración, encontrar el juego uno y el uso de reflexión para crear una instancia del tipo, si no se encuentra el registro, se lanza una excepción.

Aquí hay un código de ejemplo:

namespace Entities 
{ 

public interface IResultEntity 
{ 
} 

public class LifeEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a Life entity"); 
    } 
} 

public class PropertyEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a Property Entity"); 
    } 
} 

public class CreditCardEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a CreditCard Entity "); 
    } 
} 

public class DisabilityEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a Disability Entity"); 
    } 
} 

}

public static Entities.IResultEntity GetEntity(string entityTypeName,string fileName) 
{ 
    XDocument doc = XDocument.Load(fileName); 
    XElement element = doc.Element("TypeMappings").Elements("TypeMapping") 
           .SingleOrDefault(x => x.Attribute("name").Value == entityTypeName);   

    if(element == null) 
    { 
     throw new InvalidOperationException("No type mapping found for " + entityTypeName); 
    } 
    string typeName = element.Attribute("type").Value; 
    Type type = Type.GetType(typeName); 
    Entities.IResultEntity resultEntity = Activator.CreateInstance(type) as Entities.IResultEntity; 
    if(resultEntity == null) 
    { 
     throw new InvalidOperationException("type mapping for " + entityTypeName + " is invalid"); 
    } 
    return resultEntity; 
} 

    public static void Main() 
{ 
    try 
    { 
     Entities.IResultEntity result = GetEntity("life", @"c:\temp\entities.xml"); 
     Console.WriteLine(result); 

     result = GetEntity("property", @"c:\temp\entities.xml"); 
     Console.WriteLine(result); 

     result = GetEntity("disability", @"c:\temp\entities.xml"); 
     Console.WriteLine(result);   

     result = GetEntity("creditcard", @"c:\temp\entities.xml"); 
     Console.WriteLine(result);   

     result = GetEntity("foo", @"c:\temp\entities.xml"); 
     Console.WriteLine(result);  

    } 
} 

Una gran cantidad de marcos de DI le permiten proporcionar múltiples registros para una interfaz que se puede consulta basada en metadatos. Consulte this link sobre cómo MEF exporta usando metadatos.

+0

parece exagerado –

2

No está mal, es casi exactamente lo mismo que un ejemplo (Método de Fábrica Paramétrico) en la Biblia de la Banda de los Cuatro.

Solía ​​pensar que las declaraciones de cambio son un olor a código, no lo son, tienen su lugar en cualquier idioma de OO.

Cuestiones relacionadas