Design patterns 建造工厂的最佳方式

Design patterns 建造工厂的最佳方式,design-patterns,factory,factory-pattern,Design Patterns,Factory,Factory Pattern,最近我读了很多关于工厂模式的书。我正试图找出实现它的最佳方法。在C#Agile Principles patterns and practice一书中,建议创建如下工厂: public class ShapeFactoryImplementation : ShapeFactory { public Shape Make(string name) { if (name.Equals("Circle")) return new Circle();

最近我读了很多关于工厂模式的书。我正试图找出实现它的最佳方法。在C#Agile Principles patterns and practice一书中,建议创建如下工厂:

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

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

在第一个版本中,
ShapeFactoryImplementation
的界面保持不变。在第二种情况下,每次添加新方法时,都会有一个新的接口。

工厂模式的要点是将客户机与对象的创建分离


在第二个示例中,客户机硬编码了要创建的形状,例如
MakeCircle
,因此将对象创建和客户机紧密结合起来。

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

如果您认为在第一个方法中使用字符串来选择所需形状很难看,为什么不使用枚举呢?

这里列出的两个方法称为简单工厂,第一个更好,因为创建主要由参数而不是方法决定

事实上,工厂方法模式是官方的,假设有一个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();
    }
}
public class FooBFactory : IFooFactory
{
    public IFoo CreateFoo()
    {
         return new FooB();
    }
}
要创建FooB,我们需要:

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

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

在我看来,这两个实现都违反了至少两个规则,(好吧,你可以说它负责创建,但我认为这责任太广泛了)并且(每次添加形状时,您都需要更改类)。更不用说,第一个实现不允许您为构造函数提供参数。我解决这个问题的方法是使用以下方法:

    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);
        }
    } 
公共结构形状特征设置
{
公共谓词;
公共函数创建者;
}
公共接口IShapeCreationParams
{}
公共结构CircleCreationParams:IShapeCreationParams
{
公共CircleCreationParams(int r):this()
{
R=R;
}
公共int R{get;私有集;}
}
公共结构SquareCreationParams:IShapeCreationParams
{
public SquareCreationParams(int a):this()
{
A=A;
}
public int A{get;private set;}
}
公共类ShapeFactory:IShapeFactory
{
受保护的静态列表设置=新建列表();
静态ShapeFactory()
{
设置。添加(新形状特征设置)
{
谓词=t=>t.Equals(“圆”),
Creator=p=>新圆(((CircleCreationParams)p).R)
});
设置。添加(新形状特征设置)
{
谓词=t=>t.Equals(“平方”),
Creator=p=>newsquare(((SquareCreationParams)p).A)
});
}
公共形状创建(字符串名称,IShapeCreationParams p)
{
返回settings.FirstOrDefault(s=>s.Predicate(name)).Creator(p);
}
} 
出于示例的考虑,创建设置是使用静态构造函数设置的,这仍然需要类进行更改,在实际场景中,我会为此添加AddSettings方法,或者依赖IoC容器(如果它提供此类功能的话)

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


当然,考虑到这个简单的建筑形状示例,这似乎有点过度设计,但这是我在更复杂的现实生活场景中采用的方法。

我可以问一下你使用什么方法吗?如果我使用工厂模式,我会使用第一个示例。然而,我发现依赖注入在大多数c语言中更有用好的,这听起来很公平。那么你说的是,在大多数情况下,你更喜欢使用DI的工厂模式,而不是使用DI?这取决于具体情况,但是是的。你可以使用IFactoriedObject类型的接口,并让你的分解类继承它。你知道你正在创建的工厂类型,所以你应该能够直接使用它,而不必使用DI必须通过逻辑if/then/select/case/norris语句。第二个版本无论如何都有点愚蠢。您正在显式地命名方法(例如,
MakeSquare
MakeCircle
,…)但是,您返回的是一个抽象类型-
形状
。除了维护问题,它已经有了一种肮脏的感觉。您使用哪种工厂?+1进行强类型。当然,问题是您又回到了维护一个可接受类型的列表。这不是一个突破性的更改,在我看来,这是值得“努力”的.我相信,使用
Enum
可以让您维护的不仅仅是工厂本身。