Использование фабричного метода / абстрактного фабричного шаблона - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть следующее упрощенное требование синтаксического анализа файла инвентаризации, который содержит букву марки автомобиля и соответствующую строку спецификации в каждой строке.Например:

A Sedan
B Blue

И ниже я предоставляю упрощенную версию кода:

    class StockManager
    {
        List<ICar> cars = new List<ICar>();
        public StockManager(List<string> inventoryFileLines)
        {
            foreach(var inventoryFileLine in inventoryFileLines)
            {
                string[] parts = inventoryFileLine.Split(' ');
                cars.Add(CreateCar(parts[0], parts[1]));
            }
        }

        public decimal CalculateTotal()
        {
            decimal total = 0;
            foreach(var car in cars)
            {
                total += car.GetPrice();
            }
            return total;
        }

        public ICar CreateCar(string brand, string spec)
        {
            if(brand == "A")
            {
                return new CarA(spec);
            }else if(brand == "B")
            {
                return new CarB(spec);
            }
            throw new Exception();
        }
    }


    interface ICar
    {
        decimal GetPrice();
    }

    class CarA : ICar
    {
        string type;

        public CarA(string type)
        {
            this.type = type;
        }
        public decimal GetPrice()
        {
            if(type == "Sedan")
            {
                return 30000;
            }
            else if (type == "SUV")
            {
                return 50000;
            }
            throw new Exception();
        }
    }

    class CarB : ICar
    {
        string color;

        public CarB(string color)
        {
            this.color = color;
        }
        public decimal GetPrice()
        {
            if (color == "Orange")
            {
                return 20000;
            }else if (color == "Red")
            {
                return 25000;
            }
            throw new Exception();
        }
    }

В будущем могут быть добавлены новые бренды и спецификации.Это изменение, которое я должен предвидеть и обеспечить гибкость.Теперь я хочу применить правильные шаблоны проектирования, но применять их по правильным причинам, а не только ради применения шаблона проектирования.(Как утверждает GoF: «Шаблон проектирования следует применять только тогда, когда действительно необходима гибкость, которую он предоставляет».)

Первое, что мне пришло в голову, - это фабричный метод или абстрактный фабричный шаблон.Поэтому, когда в будущем появится новый автомобиль марки C:

Factory Method

Сделайте CreateCar виртуальным и переопределите его в новом классе StockManager, которым я будуиспользуя:

class StockManager2 : StockManager
{
    public StockManager2(List<string> inventoryFileLines) : base(inventoryFileLines) { }
    public override ICar CreateCar(string brand, string spec)
    {
        if (brand == "C")
        {
            ...
        }
        return base.CreateCar(brand, spec);
    }
}

Абстрактная фабрика

Сделайте метод CreateCar своим собственным абстрактным фабричным классом и предоставьте его классу StockManager.


Оба эти рефакторинга выглядят великолепно, если я хочу использовать разные альтернативные варианты создания во время выполнения, например, несколько действительных CreateCar фабрик.И пример Лабиринта, приведенный GoF, также расширяет эту идею.

Но на самом деле ожидаемое изменение - это не альтернативная фабрика, а модифицированная фабрика.Поэтому мне кажется гораздо более логичным изменить метод CreateCar вместо создания нового класса фабрики и оставить старый устаревшим (здесь речь идет о методе абстрактной фабрики).То же самое относится и к созданию второго класса StockManager2 в случае метода Factory.Я знаю, что принцип Open / Closed (O SOLID Роберта Мартина) говорит не модифицировать класс, а расширять его, и шаблон фабрики делает именно это, но оправдывает ли его использование приведенный выше пример, учитывая требование расширяемости, которое я упоминал в начале?Кажется, что требование не является расширением в смысле, объясненном в GoF, а является настоящей модификацией.Но я бы хотел, чтобы меня поправили, если я ошибаюсь.

1 Ответ

0 голосов
/ 12 ноября 2018

Вы можете использовать механизм создания объекта времени выполнения. Таким образом, c # будет создан класс на основе строки. Единственное ожидание, что имя класса и данная строка должны совпадать.

Но вы можете использовать статический словарь, если значения не совпадают. Вот код:

public ICar CreateCar(string brand, string spec)
{
    System.Type type = typeof( ICar ).Assembly.GetTypes().Where( t => t.Name == brand ).FirstOrDefault();

    object instance = Activator.CreateInstance( type, new object[ 1 ] { specs } );
    return (ICar)instance;
}

Конечно, эта функция не обрабатывает никаких ошибок, но это простая часть. Кстати, в вашем коде, пожалуйста, используйте NotImplementedException вместо базового класса Exception, потому что это то, что вы хотите :) реализовать новые бренды.

...