Type Casting and the Factory pattern

前端 未结 4 1417
执笔经年
执笔经年 2020-12-14 13:15

I\'m having a hard time figuring out how to implement a factory pattern in a DTO mapper I\'m trying to create. I\'m pretty sure I need to rethink my design. Here is a very s

相关标签:
4条回答
  • 2020-12-14 13:28

    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?

    0 讨论(0)
  • 2020-12-14 13:37

    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).

    0 讨论(0)
  • 2020-12-14 13:41

    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
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-14 13:50

    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 = ...;
    
    0 讨论(0)
提交回复
热议问题