У меня есть следующее упрощенное требование синтаксического анализа файла инвентаризации, который содержит букву марки автомобиля и соответствующую строку спецификации в каждой строке.Например:
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, а является настоящей модификацией.Но я бы хотел, чтобы меня поправили, если я ошибаюсь.