键入Casting和Factory模式

时间:2010-08-26 19:15:43

标签: c# factory-pattern

我很难弄清楚如何在我正在尝试创建的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 个答案:

答案 0 :(得分:13)

当对象的实现不同而不是接口时,最好使用工厂模式。在你的情况下,工厂模式不是太有用,你可能最好直接创建对象(或者其他一些模式可能更好)。

答案 1 :(得分:10)

您可以将特定类型的方法添加到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
        }
    }
}

答案 2 :(得分:1)

使用字符串来传达你想要的类型吗?你可以改用泛型:

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

现在很难确切地说这是否有效,因为我们不知道你在 在CreatePerson中做什么的细节。如果只是调用无参数构造函数,那很简单:

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

然而,这也是毫无意义的,因为调用者可以这样做。仿制药在您的实际情况下是否可行?如果没有,你能解释一下为什么不行,我们可以尝试解决它吗?

答案 3 :(得分:1)

没有简单的方法可以解决这个问题。

要使用PilotNumber属性,您需要Pilot类型。使用工厂模式意味着您放弃了不同的Person子类型。

如果有任何安慰,BCL有类似的模式,

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