建造工厂的最佳方式

时间:2011-08-31 11:29:00

标签: design-patterns factory factory-pattern

我最近一直在阅读有关工厂模式的文章。我试图找出实现它的最佳方法。在C#Agile Principles模式和实践的书中,建议是像这样创建工厂:

public class ShapeFactoryImplementation : ShapeFactory {
    public Shape Make(string name) {
        if (name.Equals("Circle"))
            return new Circle();
        else if (name.Equals("Square"))
            return new Square();
        else
            throw new Exception("ShapeFactory cannot create: {0}", name);
        }
    }

而不是......

public class ShapeFactoryImplementation : ShapeFactory {
    public Shape MakeCircle() {
        return new Circle();
    }

    public Shape MakeSquare() {
        return new Square();
    }
}

请告诉我你的想法是什么?或者可能有更好的方法来实现工厂模式?

6 个答案:

答案 0 :(得分:3)

在第一个版本中,ShapeFactoryImplementation的界面保持不变。在第二次添加新方法时,您将拥有一个新界面。

答案 1 :(得分:3)

在我看来,这两个实现都打破了至少一些规则,Single Responsibility Principle(好吧,你可以说它负责创造,但IMO责任过于宽泛)和Open/Closed Principle(每次你添加一个你需要改变类的形状)。更不用说事实,第一个实现不允许您为构造函数提供参数。我倾向于解决这个问题的方法是使用以下方法:

    public struct ShapeCreationSettings
    {
        public Predicate<string> Predicate;
        public Func<IShapeCreationParams, Shape> Creator;
    }

    public interface IShapeCreationParams
    {}

    public struct CircleCreationParams : IShapeCreationParams
    {
        public CircleCreationParams(int r) : this()
        {
            R = r;
        }

        public int R  { get; private set; }
    }

    public struct SquareCreationParams : IShapeCreationParams
    {
        public SquareCreationParams(int a) : this()
        {
            A = a;
        }

        public int A { get; private set; }

    }

    public class ShapeFactory : IShapeFactory
    {
        protected static List<ShapeCreationSettings> settings = new List<ShapeCreationSettings>();

        static ShapeFactory()
        {
            settings.Add(new ShapeCreationSettings
            {
                Predicate = t => t.Equals("Circle"),
                Creator = p => new Circle(((CircleCreationParams) p).R)
            });
            settings.Add(new ShapeCreationSettings
            {
                Predicate = t => t.Equals("Square"),
                Creator = p => new Square(((SquareCreationParams)p).A)
            });
        }

        public Shape Create(string name, IShapeCreationParams p)
        {
            return settings.FirstOrDefault(s => s.Predicate(name)).Creator(p);
        }
    } 

为了示例,创建设置是使用静态构造函数设置的,它仍然需要更改类,在实际场景中我会为此添加AddSettings方法或依赖IoC容器(如果它提供这样的功能)。

通过这样做,您可以获得松散耦合的优势(您可以在分离时更改对象的实际构造方式),并且可以随时添加新类型的形状,甚至无需重建工厂。此外,您还可以为构造函数提供特定于形状的创建参数。

当然,考虑到这个构建形状的简单示例,这似乎过度设计了一点,但这是我为更复杂的现实生活场景所遵循的方法。

答案 2 :(得分:2)

工厂模式的要点是将客户端与对象的创建分开。

在第二个示例中,客户端对要创建的形状进行了硬编码,例如MakeCircle,因此将对象创建与客户端紧密耦合。

答案 3 :(得分:2)

如果您认为在第一种方法中使用字符串来选择所需的形状是丑陋的,为什么不使用枚举?

答案 4 :(得分:1)

这里列出的两个叫做Simple Factory,第一个更好,因为创建主要由参数而不是方法决定。

实际工厂方法模式是官方的,假设有一个IFoo,并且有两个实现FooA,FooB如下:

public interface IFoo
{
}
public class FooA : IFoo
{
}
public class FooB : IFoo
{
}

然后在客户端应用程序中使用IFoo时出现问题 - 如何创建实现实例? 存在耦合是因为客户端依赖于FooA / FooB的创建,甚至FooC的特征,所以我们需要去耦合。我们将IFoo实现实例的创建责任移到另一个接口:

public interface IFooFactory
{
    IFoo CreateFoo();
}

要创建FooA,我们需要:

public class FooAFactory : IFooFactory
{
    public IFoo CreateFoo()
    {
         return new FooA();
    }
}

要创建FooB,我们需要:

public class FooBFactory : IFooFactory
{
    public IFoo CreateFoo()
    {
         return new FooB();
    }
}

这里的效果是,当有FooC时,我们不需要修改现有的Factory,我们只需要实现另一个FooCFactory。

答案 5 :(得分:0)

我个人喜欢第一个。它允许您轻松扩展创建列表。另一方面,第二种方法依赖于不同的方法来创建新对象。如果为新形状添加方法,则会破坏ABI。您应该将这些方法更改为protected,这样新添加的内容将保持公共接口和派生类仍然可以更改对象的构造方式。

相关问题