简体   繁体   English

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

[英]Use Of Factory Method/Abstract Factory Pattern

I have the following simplified requirement of parsing an inventory file that contains the letter of the brand of car and the corresponding spec string on each line. 我有以下简化的要求,即解析包含汽车品牌字母和每行相应规格字符串的库存文件。 For example: 例如:

A Sedan
B Blue

And below I provide a simplified version of the code: 下面我提供了代码的简化版本:

    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();
        }
    }

In the future, new brands and specs might be added. 未来,可能会增加新的品牌和规格。 This is the change that I should anticipate and provide flexibility for. 这是我应该预期并提供灵活性的变化。 Now I want to apply the right design patterns but apply them for the right reasons, not just for the sake of having applied a design pattern. 现在我想应用正确的设计模式,但是出于正确的原因应用它们,而不仅仅是为了应用设计模式。 (As stated by GoF: “A design pattern should only be applied when the flexibility it affords is actually needed.”) (正如GoF所说:“只有在实际需要灵活性时才应用设计模式。”)

The first thing that came to my mind is the factory method or abstract factory pattern. 我想到的第一件事是工厂方法或抽象工厂模式。 So when there is a new car brand C is added in the future: 因此,当未来添加新品牌C时:

Factory Method 工厂方法

Make CreateCar virtual and override it in the new StockManager class that I will be using: 使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);
    }
}

Abstract Factory 抽象工厂

Make CreateCar method into its own abstract factory class and provide it to the StockManager class. 将CreateCar方法放入其自己的抽象工厂类中,并将其提供给StockManager类。


Both these refactorings look great if I want to use different alternative creation options at run-time, such as multiple valid CreateCar factories. 如果我想在运行时使用不同的替代创建选项,例如多个有效的CreateCar工厂,这两个重构都看起来很棒。 And the Maze example is given by GoF also expands on this idea. 而GoF给出的迷宫示例也扩展了这个想法。

But as a matter of fact, the change I anticipate is not an alternative factory but a modified factory. 但事实上,我预期的改变不是替代工厂,而是改造工厂。 So it seems much more logical to me to modify the CreateCar method instead of creating a new factory class and leaving the old one obsolete (speaking for the Abstract Factory method here). 因此,修改CreateCar方法而不是创建新的工厂类并使旧的工厂类过时(在此处为Abstract Factory方法说话)似乎更合乎逻辑。 And the same holds true for creating a second StockManager2 class in the case of Factory method. 对于在Factory方法的情况下创建第二个StockManager2类也是如此。 I know that Open/Closed principle (O of SOLID by Robert Martin) says not to modify the class but extend it, and the factory pattern does exactly that but does the above example justify its use, given the extensibility requirement I mentioned in the beginning? 我知道开放/封闭原则(罗伯特·马丁的SOL的O)说不要修改类但是扩展它,工厂模式确实如此,但上面的例子证明了它的使用是正确的,考虑到我在开始时提到的可扩展性要求? It seems like the requirement is not an extension in the sense explained in GoF but a true modification instead. 似乎要求不是GoF中解释的意义上的扩展,而是真正的修改。 But I would like to be corrected if I am wrong. 但如果我错了,我想纠正。

You can use the runtime object creation mechanism. 您可以使用运行时对象创建机制。 So the c# will be created the class based on the string. 所以c#将根据字符串创建类。 The only expectation the class name and the given string must be the same. 唯一的期望是类名和给定的字符串必须相同。

But you can use a static dictionary if the values are not the same. 但是如果值不相同,则可以使用静态字典。 Here is the code: 这是代码:

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;
}

of course this function does not handle any error, but it is the easy part. 当然这个函数不能处理任何错误,但它很容易。 By the way, in your code please use the NotImplementedException instead of Exception base class, because this is what you want :) implement the new brands. 顺便说一下,在你的代码中请使用NotImplementedException而不是Exception基类,因为这是你想要的:)实现新的品牌。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM