Тип литья и заводской шаблон - PullRequest
6 голосов
/ 26 августа 2010

Мне трудно понять, как реализовать фабричный шаблон в 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? это неправильный способ сделать это? Я мог бы поместить номер пилота в класс Person, но тогда Employee унаследовал бы номер, а это не то, что я хочу. Что я могу сделать?

Спасибо!

-Jackson

Ответы [ 4 ]

13 голосов
/ 26 августа 2010

Заводской шаблон лучше всего использовать, когда объекты отличаются по реализации, а не по интерфейсу. В вашем случае фабричный шаблон не слишком полезен, и вам, вероятно, лучше создавать свои объекты напрямую (или, возможно, лучше другой шаблон).

10 голосов
/ 26 августа 2010

Вы можете добавить методы для определенных типов в свой класс PersonFactory или добавить общий метод CreatePerson<T>(), но это будет полезно, только если вызывающая сторона уже знает, какого типа человек должен получать.Может быть, это так, или, может быть, нет.

При таком сценарии я бы ожидал, что код, который фактически выполняет вызов PersonFactory.CreatePerson, не будет знать или не заботиться о том, какого типа человек возвращается.Если после этого момента у вас есть какой-то код, который уже знает или выясняет, какой тип объекта «человек» у вас есть, то вам просто придется его привести.

Ниже приведен пример кода, который иллюстрирует, что вы можете сделать с вашим объектом.заводские и другие сценарии использования, пытаясь объяснить, когда вам просто нужно разыграть, а когда нет.

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
        }
    }
}
1 голос
/ 26 августа 2010

Нет простого способа обойти это.

Чтобы использовать свойство PilotNumber, вам нужен тип Pilot. Использование шаблона Factory означает, что вы отказываетесь от различных подтипов Person.

Если это утешает, BCL имеет аналогичные модели,

 var req = WebRequest.CreateRequest("http://someUrl");
 ((HttpWebRequest)req).Contentlenght = ...;
1 голос
/ 26 августа 2010

У вас есть , чтобы использовать строку для передачи нужного вам типа?Вместо этого вы могли бы использовать дженерики:

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

Теперь трудно точно сказать, сработает ли это или нет, потому что мы не знаем деталей того, что вы бы действительно делаливнутри CreatePerson.Если это просто , вызывающий конструктор без параметров, это легко:

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

Однако это также довольно бессмысленно, поскольку вызывающая сторона может сделать это вместо этого.Являются ли генерики жизнеспособными в вашей реальной ситуации?Если нет, не могли бы вы объяснить, почему нет, и мы могли бы попытаться обойти это?

...