Мне трудно понять, как реализовать фабричный шаблон в DTO-картографе, который я пытаюсь создать. Я уверен, что мне нужно переосмыслить свой дизайн. Вот очень маленький пример того, к чему я прибегаю:
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();
}
}
}
и использования фабрики:
Person thePilot = PersonFactory.CreatePerson("Pilot");
((Pilot)thePilot).PilotNumber = "123ABC";
Как мне обойти загрузку номера пилота, не вводя его в Pilot ?? это неправильный способ сделать это? Я мог бы поставить номер пилота в классе Персона, но тогда сотрудник унаследует число, а это не то, что я хочу. Что я могу сделать?
Спасибо!
-Jackson
Вы можете добавить методы для конкретных типов в свой класс PersonFactory или добавить общий метод CreatePerson
, но это будет полезно, только если вызывающий объект уже знает, какого человека он должен принимать. Может быть, это так, а может быть, и нет.
При таком сценарии я ожидаю, что код, который фактически вызывает PersonFactory.CreatePerson, не будет знать и не заботится о том, какой человек возвращается. Если у вас есть некоторый код после этого момента, который уже знает или вычисляет, какой у вас тип объекта person, тогда вам просто нужно его привести.
Ниже приведен пример кода, который иллюстрирует, что вы можете сделать на своей фабрике, и различные сценарии использования, пытаясь объяснить, когда вам просто нужно приведение, а когда нет.
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
}
}
}
Фабричный шаблон лучше всего использовать, когда объекты различаются реализацией, а не интерфейсом. В вашем случае фабричный шаблон не слишком полезен, и вам, вероятно, лучше создавать свои объекты напрямую (или, может быть, какой-то другой шаблон лучше).
Это непросто.
Чтобы использовать свойство PilotNumber, вам нужен тип Pilot. Использование шаблона Factory означает, что вы отказываетесь от различных подтипов Person.
Если вас это утешит, в BCL есть похожие шаблоны,
var req = WebRequest.CreateRequest("http://someUrl");
((HttpWebRequest)req).Contentlenght = ...;
Должны ли вы использовать строку для передачи нужного вам типа? Вместо этого вы можете использовать дженерики:
public static T CreatePerson<T>() where T : Person
Теперь трудно сказать точно, будет ли это работать или нет, потому что мы не знаем деталей того, что вы на самом деле будете делать в CreatePerson. Если это просто вызов конструктора без параметров, это легко:
public static T CreatePerson<T>() where T : Person, new()
{
return new T();
}
Однако это также разумно бессмысленно, поскольку вместо этого это может сделать вызывающая сторона. Жизнеспособны ли дженерики в вашей реальной ситуации? Если нет, не могли бы вы объяснить, почему, и мы могли бы попытаться обойти это?