Приведение типов и фабричный шаблон

Мне трудно понять, как реализовать фабричный шаблон в 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

6
задан adminJaxon 26 August 2010 в 19:15
поделиться

4 ответа

Вы можете добавить методы для конкретных типов в свой класс 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
        }
    }
}
10
ответ дан 8 December 2019 в 04:27
поделиться

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

13
ответ дан 8 December 2019 в 04:27
поделиться

Это непросто.

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

Если вас это утешит, в BCL есть похожие шаблоны,

 var req = WebRequest.CreateRequest("http://someUrl");
 ((HttpWebRequest)req).Contentlenght = ...;
1
ответ дан 8 December 2019 в 04:27
поделиться

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

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

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

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

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

1
ответ дан 8 December 2019 в 04:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: