How to implement an elegant animal farm?

Is there an elegant way (using generics and / or an activator and / or any other nice trick) to implement a factory object other than a big switch for the next scenario?

enum AnimalKind
{
    Bear,
    Fox,
    Ant,
}

abstract class Animal
{
    abstract AnimalKind Kind {get;}
    ...
    static Animal Create(AnimalKind kind) // Factory method, convert kind to animal
    {
        switch (kind)
        {
            case AnimalKind.Bear: return new Bear();
            ...
            // Update this part anytime the new AnimalKind is added
        }
    }
}

class Bear : Animal
{
    override AnimalKind Kind {get {return AnimalKind.Bear;}}
    ...
}
...

      

Unfortunately, the kind of animal - a requirement , and the desired goal is the following conversion: List<AnimalKind> => List<Animal>

. Otherwise, something like T Create<T>() where T:Animal

. Since new animals are encountered in the system, the fewer code updates the better (the ideal case is an enum and the animal class itself, nothing in the factory method).

Edit: Based on the comments below, the best answer is here !

Edit 2:

  • Since I can answer the question closed as a duplicate,
  • Since the referenced answer does not satisfy me,
  • Since there is no problem of poor performance due to the reflection of the farm animals,
  • Also for people who wanted to use "working examples" for copy / paste

I am posting my solution here. Feel free to modify and / or improve and / or simplify it:

Animal.cs:

using System;
using System.Linq;
using System.Reflection;

namespace ReflectionFactory
{
    public enum AnimalKind
    {
        None,
        Bear,
        Fox, 
        Ant,
    }

    public abstract class Animal
    {
        public const AnimalKind StaticKind = AnimalKind.None;
        public abstract AnimalKind Kind {get;}

        // That is the "trick" I was looking for: kind => Animal, no further changes
        public static Animal Create(AnimalKind kind)
        {
            var asm  = Assembly.GetExecutingAssembly();
            var type = asm.GetTypes()
                          .Where(t => t.IsSubclassOf(typeof(Animal)))
                          .FirstOrDefault(t => (AnimalKind)(t.GetField("StaticKind").GetValue(null)) == kind);

            // FIXME: Might want to check for unwanted type values (like null)

            return (Animal)Activator.CreateInstance(type);
        }
    }

    public class Bear : Animal
    {
        public new const AnimalKind StaticKind = AnimalKind.Bear;
        public override AnimalKind Kind {get{return StaticKind;}}
        // ...
   }

    public class Fox : Animal
    {
        public new const AnimalKind StaticKind = AnimalKind.Fox;
        public override AnimalKind Kind {get{return StaticKind;}}
        // ...
    }

    public class Ant : Animal
    {
        public new const AnimalKind StaticKind = AnimalKind.Ant;
        public override AnimalKind Kind {get{return StaticKind;}}
        // ...
    }
}

      

Program.cs:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ReflectionFactory
{
    class Program
    {
        static void Main(string[] args)
        {
            var shoppingList = new List<AnimalKind>
            {
                AnimalKind.Bear,
                AnimalKind.Bear,
                AnimalKind.Fox,
                AnimalKind.Ant,
                AnimalKind.Ant,
                AnimalKind.Ant,
            };

            var animalFarm = shoppingList.Select(Animal.Create);

            foreach (var animal in animalFarm)
            {
                Console.WriteLine(animal.Kind);
            }
        }
    }
}

      

+3


source to share





All Articles