Заводская модель, которая будет удовлетворять принципу Open / Closed - PullRequest
12 голосов
/ 24 октября 2011

У меня есть следующие конкретные Animal продукты: Dog и Cat.

Я использую параметризованный фабричный метод для создания указанных продуктов. В зависимости от параметра AnimalInfo, который передается методу Factory, будет создан конкретный продукт. Логика сопоставления помещается в метод Factory.

Вот мой код:

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

Вот мой метод Factory с его логикой:

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

Проблема, которую я вижу здесь, заключается в том, что сам метод Factory нарушает принцип Open / Closed таким образом, что если я добавлю новое Animal, мне нужно будет изменить метод Factory, чтобы он отражал новое отображение.

Есть ли способ сделать творение более "динамичным" с помощью рефлексии? Что еще более важно, есть ли анти-шаблон в моем дизайне?

Ответы [ 3 ]

18 голосов
/ 24 октября 2011

Позвольте мне немного обойти. ТВЕРДЫЕ принципы хороши. Но поймите, что в какой-то момент принципы рушатся, факт, который признает даже создатель термина SOLID. Да, вы хотите следовать одиночной ответственности, открывать / закрывать и т. Д., Но когда вы делаете это, что-то должно знать, как создавать все те вещи, которые в противном случае красиво отделены от одной ответственности.

Подумайте об одной из вещей, которую сказал дядя Боб, если и переключатся в вашем коде. "Имейте это точно один раз." Само собой разумеется, что длинный if или switch действительно будет нарушением SRP и OCP. И это нормально, , если у вас есть это нарушение один раз.

Итак, держите ваш

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

И получите его один раз. Да, это нарушение OCP. Да, это может нарушить SRP. Но что-то где-то должно. Ключ заключается в уменьшении количества этих чего-то и тех, кто-то.

10 голосов
/ 24 октября 2011

Самый простой выход - сделать AnimalInfo самой фабрикой:

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

Реализация для DogInfo:

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

    public override Dog CreateAnimal()
    {
        return new Dog(this);
    }
}

Вы можете сохранить свой текущий статический Factory, если хотите:

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

Не совсем строгое соблюдение паттерна Factory, IMO, но больше не нарушает ваш принцип открытия / закрытия.

0 голосов
/ 24 октября 2011

Если вы ищете метод, основанный на отражении, вы можете сделать что-то вроде следующего ...

public static class AnimalFactory
{
    public static Animal CreateAnimal(Type animalType)
    {
        return Activator.CreateInstance(animalType) as Animal;
    }

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


}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...