Type Casting and the Factory pattern

时光怂恿深爱的人放手 提交于 2019-11-28 22:01:27

You could add methods for specific types to your PersonFactory class, or add a generic CreatePerson<T>() method, but that would only be useful if the caller already knows what type of person it should be receiving. Maybe this is the case, or maybe not.

With this scenario, I'd expect that the code that is actually making the call to PersonFactory.CreatePerson would not know or care what kind of person is being returned. If you have some code after that point that already knows or figures out what type of person object you have, then you will simply have to cast it.

Below is a code example that illustrates what you could do on your factory and different usage scenarios, attempting to explain when you simply need to cast or when you don't.

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
        }
    }
}

The factory pattern is best used when the objects differ in implementation, not interface. In your case the factory pattern is not too beneficial, and you are probably better off creating your objects directly (or some other pattern maybe better).

Do you have to use a string to communicate the type you want? You could use generics instead:

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

Now it's hard to say exactly whether this will work or not, because we don't know the details of what you'd really be doing within CreatePerson. If it's just calling a parameterless constructor, that's easy:

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

However, that's also reasonably pointless as the caller could do that instead. Is generics viable in your actual situation? If not, could you explain why not, and we could try to work around it?

There is no easy way around this.

In order to use the PilotNumber property you need the Pilot type. Using a Factory pattern means you are giving up the different sub-types of Person.

If it's any consolation, the BCL has similar patterns,

 var req = WebRequest.CreateRequest("http://someUrl");
 ((HttpWebRequest)req).Contentlenght = ...;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!