使用工厂方法/抽象工厂模式

时间:2018-11-12 20:11:34

标签: java c# design-patterns

我对解析清单文件有以下简化的要求,该清单文件包含汽车品牌的字母和每行上的相应规格字符串。例如:

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方法而不是创建一个新的工厂类并废弃旧的工厂类(在这里说“抽象工厂”方法)似乎更加合乎逻辑。对于Factory方法,在创建第二个StockManager2类时也是如此。 我知道Open / Closed原则(Robert Martin的SOLID的O)说不要修改类而是对其进行扩展,并且鉴于我在开头提到的可扩展性要求,工厂模式确实做到了这一点,但是上面的示例证明了它的使用合理性。 ?似乎要求不是GoF中解释的扩展,而是真正的修改。但是,如果我错了,我想纠正。

1 个答案:

答案 0 :(得分:0)

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