Design patterns 建造工厂的最佳方式
最近我读了很多关于工厂模式的书。我正试图找出实现它的最佳方法。在C#Agile Principles patterns and practice一书中,建议创建如下工厂: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();
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
可以让您维护的不仅仅是工厂本身。