7

我很难弄清楚如何在我试图创建的 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 会继承这个号码,这不是我想要的。我能做些什么?

谢谢!

-杰克逊

4

4 回答 4

14

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

于 2010-08-26T19:22:56.017 回答
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
        }
    }
}
于 2010-08-26T19:27:31.803 回答
1

您是否必须使用字符串来传达您想要的类型?您可以改用泛型:

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

现在很难确切地说这是否会起作用,因为我们不知道您在 CreatePerson 中真正要做的事情的细节。如果它只是调用一个无参数的构造函数,那很容易:

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

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

于 2010-08-26T19:22:59.683 回答
1

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

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

如果有什么安慰的话,BCL 有类似的模式,

 var req = WebRequest.CreateRequest("http://someUrl");
 ((HttpWebRequest)req).Contentlenght = ...;
于 2010-08-26T19:24:50.713 回答