【发布时间】:2018-11-12 20:11:34
【问题描述】:
我有以下简化的解析库存文件的要求,该文件包含汽车品牌的字母和每行对应的规格字符串。例如:
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时:
工厂方法
将 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给出的Maze例子也扩展了这个想法。
但事实上,我预期的变化不是替代工厂,而是修改后的工厂。所以对我来说修改CreateCar 方法似乎更合乎逻辑,而不是创建一个新的工厂类并让旧的工厂类过时(这里说的是抽象工厂方法)。在工厂方法的情况下创建第二个 StockManager2 类也是如此。
我知道 Open/Closed 原则(Robert Martin 的 SOLID 的 O)说不要修改类而是扩展它,工厂模式正是这样做的,但是上面的例子证明了它的使用是合理的,因为我在开头提到了可扩展性要求?似乎该要求不是 GoF 中解释的意义上的扩展,而是真正的修改。但如果我错了,我希望得到纠正。
【问题讨论】:
-
您在其他地方填写inventoryFileLines。你为什么不在那里创建你的汽车并用 List
而不是 List 填充 StockManager?改变 StockManager 的唯一原因是管理库存的其他一些责任。 -
@thomas 我不明白将这个逻辑转移到另一个地方会如何改变这种情况。
-
这就是结构模式的精髓 :-) 您可以创建一个工厂来加载文件并创建一些汽车。更改工厂的唯一原因是不同的文件格式或新车。
-
如果你使用装饰器,你可以避免创建新的汽车类。
-
我认为“工厂”一词的使用造成了一些混乱。您所描述的更像是一个读取字符串并返回汽车的类。而且您想对需要汽车的类隐藏所有这些实现细节。开始的一种方法是描述需要汽车的类想要做什么,并为它编写一个接口。
public interface ICarReader { ICar Read(string value); }从需要的类的角度写接口。然后你可以弄清楚你想要你的实现是什么。
标签: java c# design-patterns