2010-08-26 17 views
5

Me está costando trabajo encontrar la manera de implementar un patrón de fábrica en un mapeador DTO que intento crear. Estoy bastante seguro de que necesito volver a pensar en mi diseño. Aquí es un ejemplo muy pequeño de lo que estoy corriendo para:Tipo Casting y el patrón Factory

public abstract class Person 
{ 
    public string Name { get; set; } 
    public decimal Salary { get; set; } 
} 

public class Employee : Person 
{ 
    public Employee() 
    { 
     this.Salary = 20000; 
    } 

} 

public class Pilot : Person 
{ 
    public string PilotNumber { get; set; } 

    public Pilot() 
    { 
     this.Salary = 50000; 
    } 
} 

public static class PersonFactory 
{ 
    public static Person CreatePerson(string typeOfPerson) 
    { 
     switch (typeOfPerson) 
     { 
      case "Employee": 
       return new Employee(); 
      case "Pilot": 
       return new Pilot(); 
      default: 
       return new Employee(); 
     } 
    } 
} 

y utilizar la fábrica:

Person thePilot = PersonFactory.CreatePerson("Pilot"); 
     ((Pilot)thePilot).PilotNumber = "123ABC"; 

cómo consigo alrededor de cargar el número piloto sin typecasting al piloto? ? ¿Es esta la manera incorrecta de hacer esto? Podría poner el número piloto en la clase Person, pero luego Employee heredará el número y eso no es lo que quiero. ¿Que puedo hacer?

Gracias!

-Jackson

+0

Respondiendo las palabras "DTO mapper": http: //automapper.codeplex.com/ –

+0

gracias! esto fue útil! – adminJaxon

Respuesta

9

Se podría añadir métodos para tipos específicos de su clase PersonFactory, o añadir un método genérico CreatePerson<T>(), pero eso sólo sería útil si la persona que llama ya sabe qué tipo de persona debería recibir. Tal vez este es el caso, o tal vez no.

Con este escenario, esperaría que el código que está realizando la llamada a PersonFactory.CreatePerson no sabría ni se interesaría en qué tipo de persona se está devolviendo. Si tiene algún código después de ese punto que ya sabe o se da cuenta qué tipo de persona objeto tiene, entonces simplemente tendrá que lanzarlo.

A continuación se muestra un ejemplo de código que ilustra lo que podría hacer en su fábrica y diferentes escenarios de uso, intentando explicar cuándo simplemente necesita transmitir o cuándo no.

public static class PersonFactory 
{ 
    public static Person CreatePerson() 
    { 
     return new Person(); 
    } 

    public static Employee CreateEmployee() 
    { 
     return new Employee(); 
    } 

    public static Pilot CreatePilot() 
    { 
     return new Pilot(); 
    } 

    public static T CreatePerson<T>() 
     where T : Person 
    { 
     return (T)CreatePerson(typeof(T)); 
    } 

    public static Person CreatePerson(Type type) 
    { 
     if (type == typeof(Person)) 
      return CreatePerson(); 
     else if (type == typeof(Employee)) 
      return CreateEmployee(); 
     else if (type == typeof(Pilot)) 
      return CreatePilot(); 
     else 
      throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, "Unrecognized type [{0}]", type.FullName), "type"); 
    } 

    public static Person CreatePerson(string typeOfPerson) 
    { 
     switch (typeOfPerson) 
     { 
      case "Employee": 
       return CreateEmployee(); 
      case "Pilot": 
       return CreatePilot(); 
      default: 
       return CreateEmployee(); 
     } 
    } 
} 



class UsageExample 
{ 
    Person GetPerson() 
    { 
     Pilot p; 
     p = (Pilot)PersonFactory.CreatePerson("Pilot"); // this code already knows to expect a Pilot, so why not just call CreatePilot or CreatePerson<Pilot>()? 
     p = PersonFactory.CreatePilot(); 
     p = PersonFactory.CreatePerson<Pilot>(); 
     return p; 
    } 

    Person GetPerson(Type personType) 
    { 
     Person p = PersonFactory.CreatePerson(personType); 
     // this code can't know what type of person was just created, because it depends on the parameter 
     return p; 
    } 

    void KnowledgableCaller() 
    { 
     Type personType = typeof(Pilot); 

     Person p = this.GetPerson(typeof(Pilot)); 
     // this code knows that the Person object just returned should be of type Pilot 

     Pilot pilot = (Pilot)p; 
     // proceed with accessing Pilot-specific functionality 
    } 

    void IgnorantCaller() 
    { 
     Person p = this.GetPerson(); 
     // this caller doesn't know what type of Person object was just returned 

     // but it can perform tests to figure it out 
     Pilot pilot = p as Pilot; 
     if (pilot != null) 
     { 
      // proceed with accessing Pilot-specific functionality 
     } 
    } 
} 
+0

Me gusta el método IgnoarantCaller. ¡Puedo usar eso! Voy a cargar muchos registros de una tabla por tipo tabla por jerarquía. Si la persona tiene un "Piloto" en su columna "Tipo de persona", luego cargue el número piloto de la tabla Piloto. Si la fila tiene un tipo "Empleado" en el campo Tipo Persona, entonces no cargue el piloto número ... – adminJaxon

12

El patrón de la fábrica se utiliza mejor cuando los objetos difieren en su aplicación, no interfaz. En su caso, el patrón de fábrica no es demasiado beneficioso, y probablemente sea mejor que cree sus objetos directamente (o algún otro patrón mejor).

+0

¿Puede explicar esto un poco más @Matt Greer? 'los objetos difieren en la implementación, no en la interfaz' -> ¿cómo se aplica esto a este caso? –

+2

@Zortkun se aplica porque solo los Pilotos tienen un 'PilotNumber'. Entonces, la única manera de establecerlo es con un yeso, derrotando el propósito de la fábrica. –

1

¿Tiene tiene para usar una cuerda para comunicar el tipo que desea? Usted podría utilizar los genéricos en su lugar:

public static T CreatePerson<T>() where T : Person 

Ahora es difícil decir exactamente si esto funcionará o no, porque no sabemos los detalles de lo que se realmente estar haciendo dentro de CreatePerson. Si se trata de simplemente llamar a un constructor sin parámetros, eso es fácil:

public static T CreatePerson<T>() where T : Person, new() 
{ 
    return new T(); 
} 

Sin embargo, eso también es bastante inútil como la persona que llama podría hacer eso en su lugar. ¿Los genéricos son viables en su situación real? Si no, ¿podría explicar por qué no, y podríamos tratar de evitarlo?

+0

Los genéricos pueden funcionar en este caso. Básicamente tengo una tabla de base de datos que tengo una tabla por jerarquía de tipos. Digamos un lugar Persona con un campo "Tipo de persona". y otra tabla con información específica "piloto". Para obtener un conjunto de personas de resultado si se recorre la lista de filas de la base de datos, si es del tipo "Piloto", cargue la información de la tabla piloto, si es del tipo "Empleado" cargue esa información específica. Pero DON "T carga cosas piloto en objetos de empleados ... ¿eso lo hace un poco más claro? ¡Gracias! – adminJaxon

1

No hay una manera fácil de evitar esto.

Para utilizar la propiedad PilotNumber necesita el tipo de Piloto. Usar un patrón de Fábrica significa que estás abandonando los diferentes subtipos de Persona.

Si te sirve de consuelo, el BCL tiene patrones similares,

var req = WebRequest.CreateRequest("http://someUrl"); 
((HttpWebRequest)req).Contentlenght = ...; 
Cuestiones relacionadas