【问题标题】:Type Casting and the Factory pattern类型铸造和工厂模式
【发布时间】:2010-08-26 19:15:43
【问题描述】:

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

谢谢!

-杰克逊

【问题讨论】:

标签: c# factory-pattern


【解决方案1】:

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

【讨论】:

  • 你能详细说明一下吗@Matt Greer? '对象的实现不同,而不是接口'-->这如何适用于这种情况?
  • @Zortkun 它适用,因为只有飞行员有PilotNumber。所以设置它的唯一方法是使用演员表,违背了工厂的目的。
【解决方案2】:

您可以为您的 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
        }
    }
}

【讨论】:

  • 我喜欢 IgnoarantCaller 方法。我也许可以使用它!我将从每个层次结构的每个类型表中加载一堆记录。如果此人的“PersonType”列中有“Pilot”,则从 Pilot 表中加载飞行员编号。如果该行在 PersonType 字段中具有“员工”类型,则不要加载飞行员编号。 ..
【解决方案3】:

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

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

现在很难确切地说这是否会起作用,因为我们不知道您在 CreatePerson 中真正会做什么的详细信息。如果是只是调用一个无参数的构造函数,那很简单:

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

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

【讨论】:

  • 泛型可能在这种情况下工作。我基本上有一个数据库表,每个类型层次结构都有一个表。假设一个带有“PersonType”字段的 Person 地方。和另一个带有“试点”特定信息的表格。要获得一组人员是否会遍历数据库行列表,如果它是“Pilot”类型,则加载试点表信息,如果它是“Employee”类型,则加载该特定信息。但是不要将飞行员的东西加载到员工对象中……这样会更清楚吗?谢谢!
【解决方案4】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2023-04-04
    • 2018-08-30
    • 2014-12-26
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 2021-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多