Un modèle d’usine qui satisfera le principe d’ouverture / fermeture?

J’ai les produits Animal concrets suivants: Dog et Cat .

J’utilise une méthode d’usine paramétrée pour créer lesdits produits. En fonction du paramètre AnimalInfo transmis à la méthode Factory, un produit concret sera créé. La logique de mappage est placée dans la méthode Factory.

Voici mon code:

  public abstract class AnimalInfo { public abstract Ssortingng Sound { get; } } public class DogInfo : AnimalInfo { public override ssortingng Sound { get { return "Bark"; } } } public class CatInfo : AnimalInfo { public override ssortingng 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); } } 

Voici ma méthode Factory avec sa logique:

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

Le problème que je vois ici est que la méthode Factory elle-même viole le principe Ouvert / Fermé de telle sorte que si j’ajoute un nouvel animal, je devrais modifier la méthode Factory pour refléter le nouveau mapping.

Existe-t-il un moyen de rendre la création plus “dynamic” via la reflection? Plus important encore, y a-t-il un anti-motif dans ma conception?

La solution de facilité consiste à faire d’ AnimalInfo l’usine:

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

Implémentation pour DogInfo:

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

Vous pouvez conserver votre usine statique actuelle si vous souhaitez:

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

Ce n’est pas une ssortingcte adhésion au modèle Factory, IMO, mais cela ne contrevient plus à votre principe d’ouverture / fermeture.

Laissez-moi éviter un peu. Les principes SOLID sont bons. Mais réalisez à un moment donné, les principes s’effondrent, un fait même reconnu par le créateur du terme SOLID. Oui, vous voulez suivre une seule responsabilité, ouverte / fermée, etc., mais lorsque vous le faites, quelque chose doit savoir comment créer toutes ces choses qui sont par ailleurs bien découplées avec des responsabilités uniques.

Pensez à l’une des choses que mon oncle Bob a dites concernant les ifs et les commutateurs de votre code. “Avoir exactement une fois.” Il va de soi que le long if ou le switch sera en effet une violation de SRP et OCP. Et ce n’est pas grave, si vous avez cette violation une fois.

Alors allez-y, ayez votre

 if (a) return x; else if (b) return y; else if (c) return z; else throw new InvalidOperationException(); 

Et l’avoir une fois. Oui, c’est une violation de l’OCP. Oui, cela pourrait violer le SRP. Mais quelque chose doit quelque part . La clé est de réduire le nombre de ces personnes et de ces personnes.

Si vous recherchez une méthode basée sur la reflection, vous pouvez faire quelque chose comme ce qui suit …

 public static class AnimalFactory { public static Animal CreateAnimal(Type animalType) { return Activator.CreateInstance(animalType) as Animal; } public static Animal CreateAnimal(ssortingng animalType) { Type type = Assembly.GetExecutingAssembly().GetType(animalType); return Activator.CreateInstance(type) as Animal; } }