Search code examples
c#reflectionfactory-patternanti-patterns

A Factory Pattern that will satisfy the Open/Closed Principle?


I have the following concrete Animal products: Dog and Cat.

I am using a parameterized Factory method to create said products. Depending on the AnimalInfo parameter that is passed to the Factory method, a concrete product will be created. The mapping logic is placed in the Factory method.

Here is my code:

 public abstract class AnimalInfo
    {
        public abstract String Sound { get; }
    }

    public class DogInfo : AnimalInfo
    {
        public override string Sound
        {
            get { return "Bark"; }
        }
    }

    public class CatInfo : AnimalInfo
    {
        public override string Sound
        {
            get { return "Meow"; }
        }
    }

    public abstract class Animal
    {
        public abstract void Talk();
    }

    public class Dog : Animal
    {
        private readonly DogInfo _info;

        public Dog(DogInfo aInfo)
        {
            _info = aInfo;
        }

        public override void Talk()
        {
            Console.WriteLine(_info.Sound);
        }
    }

    public class Cat : Animal
    {
        private readonly CatInfo _info;

        public Cat(CatInfo aInfo)
        {
            _info = aInfo;
        }

        public override void Talk()
        {
            Console.WriteLine(_info.Sound);
        }
    }

Here's my Factory method with its logic:

public static class AnimalFactory
{
    public static Animal CreateAnimal(AnimalInfo aInfo)
    {
        if (aInfo is DogInfo)
            return new Dog(aInfo as DogInfo);
        if (aInfo is CatInfo)
            return new Cat(aInfo as CatInfo);
        return null;
    }
}

The problem I'm seeing here is that the Factory method itself violates the Open/Closed principle in such a way that if I add a new Animal, I will need to modify the Factory method to reflect the new mapping.

Is there a way to make the creation more "dynamic" via reflection? More importantly, is there any anti-pattern in my design?


Solution

  • The easy way out is to make AnimalInfo itself the factory:

    public abstract class AnimalInfo<T> where T: Animal
    {
        public abstract String Sound { get; }
        public abstract T CreateAnimal();
    }
    

    Implementation for DogInfo:

    public class DogInfo : AnimalInfo<Dog>
    {
        public override string Sound
        {
            get { return "Bark"; }
        }
    
        public override Dog CreateAnimal()
        {
            return new Dog(this);
        }
    }
    

    You could keep your current static Factory if you wanted to:

    public static class AnimalFactory
    {
        public static Animal CreateAnimal(AnimalInfo aInfo)
        {       
            return aInfo.CreateAnimal();
        }
    }
    

    Not exactly strict adherance to the Factory pattern, IMO, but no longer violates your open/close principle.