【问题标题】:Use Of Factory Method/Abstract Factory Pattern使用工厂方法/抽象工厂模式
【发布时间】: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


【解决方案1】:

您可以使用运行时对象创建机制。所以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 基类,因为这是你想要的:) 实现新品牌。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2014-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多