我对 factory 的理解是它封装了所有继承公共抽象类或接口的具体类的实例化。这允许客户端与确定要创建哪个具体类的过程分离,这反过来意味着您可以从程序中添加或删除具体类,而无需更改客户端的代码。你可能不得不改变工厂,但工厂是“专门建造的”,实际上只有一个改变的理由 - 添加/删除了一个具体的类。
我已经构建了一些实际上封装了对象实例化的类,但原因不同:对象很难实例化。目前,我将这些类称为“工厂”,但我担心这可能是一种误称,并会混淆其他可能会查看我的代码的程序员。我的“工厂”类没有决定实例化哪个具体类,它们保证一系列对象将以正确的顺序实例化,并且当我调用new()
时,正确的类将被传递给正确的构造函数。
例如,对于我正在处理的MVVM项目,我编写了一个类来确保我的SetupView
被正确实例化。它看起来像这样:
public class SetupViewFactory
{
public SetupView CreateView(DatabaseModel databaseModel, SettingsModel settingsModel, ViewUtilities viewUtilities)
{
var setupViewModel = new SetupViewModel(databaseModel, settingsModel, viewUtilities);
var setupView = new SetupView();
setupView.DataContext = setupViewModel;
return setupView;
}
}
将此称为“工厂”是否令人困惑?它不会决定几个可能的具体类,并且它的返回类型不是接口或抽象类型,但它确实封装了对象实例化
如果它不是“工厂”,它是什么?
修改
很多人都认为这实际上是构建器模式。
来自dofactory的构建器模式的定义如下:
将复杂对象的构造与其表示分开,以便相同的构造过程可以创建不同的表示。
这似乎也有点延伸。困扰我的是“不同的表现形式”。我的目的不是抽象构建视图的过程(不是那个不值得的目标)。我只是想在一个地方设置创建特定视图的逻辑,这样如果任何成分或创建该视图的过程发生变化,我只需要更改这一个类。构建器模式似乎真的在说,“让我们创建一个创建视图的一般过程,然后您可以为您创建的任何视图执行相同的基本过程。”很好,但那不是我在这里做的。
修改2
我刚在Wikipedia article on Dependency Injection找到了一个有趣的例子。
在“Manually Injected依赖”下,它包含以下类:
public class CarFactory {
public static Car buildCar() {
return new Car(new SimpleEngine());
}
}
这几乎完全我的用法(并且,不,我没有写维基文章:))。
有趣的是,这是此代码后面的评论:
在上面的代码中,工厂负责组装汽车,并将发动机注入汽车。这让汽车免于了解如何制造发动机,尽管现在CarFactory必须知道发动机的结构。可以创建一个EngineFactory,但这只会创建工厂之间的依赖关系。所以问题仍然存在,但至少已转移到工厂或构建器对象。 [我的重点]
所以,这告诉我,至少我不是唯一一个想创建一个类来帮助将东西注入另一个类的人,而我并不是唯一一个试图将这个类命名为工厂的人。
答案 0 :(得分:4)
在我看来,你所拥有的更多是 Builder 模式而不是工厂模式。
答案 1 :(得分:4)
对于我而言,这与SetupView实例的构造函数没什么不同。也许从你的例子中省略了太多的代码上下文,但是我不明白为什么你在那里拥有的东西不能简单地写成构造函数,并且参数作为args传递给构造函数。
严格按照GoF模式判断,我认为它错过了Factory上的标记有两个原因:
关于Builder,我认为它在产品差异方面缺失。
当我教授设计模式课程时,我告诉学生在尝试确定“最适合”时,不仅要考虑模式的意图,而应该考虑适用性和结构/参与者。意图可以帮助你排除事情,但适用性和结构/参与者将帮助你回家。
如果您可以确定您的设计的哪个部分在(抽象)工厂或构建器中履行参与者的各种角色,那么您已经为使用该命名法做了很好的案例。
答案 2 :(得分:1)
GoF称之为“Builder”
答案 3 :(得分:1)
我认为,这是一个“工厂”。当我查看类声明时,我很高兴看到这是一个“工厂”,而且它是SetupView对象的“工厂”。所以我知道这个工厂将为我提供具体且正确构建的SetupView实例。
答案 4 :(得分:1)
“工厂”是封装新对象实例化的任何东西。为什么或对象是什么并不重要。最常见的用途是如您所述,但其他不符合该描述的用途也可称为工厂。基本的想法,(我认为除了最肛门或挑剔的开发人员之外不会误解),就是为你实例化新对象......
答案 5 :(得分:1)
这对我来说似乎有点奇怪。它不是一个抽象的工厂,但它也不太适合Builder模式。对我来说最有意义的不是使用CreateView方法的SetupViewFactory,而是将此方法移动到SetupView类并使其成为静态。所以你有......
public class SetupView
{
public static SetupView CreateView(DatabaseModel databaseModel, SettingsModel settingsModel, ViewUtilities viewUtilities)
{
var setupViewModel = new SetupViewModel(databaseModel, settingsModel, viewUtilities);
var setupView = new SetupView();
setupView.DataContext = setupViewModel;
return setupView;
}
}
现在您拥有所谓的静态工厂或工厂方法。您可以更进一步并对SetupView构造函数进行私有化,以便获取SetupView实例的唯一方法是调用工厂方法。
回应OP的评论:
如果需要分离,我会避免在代码的这一部分的任何地方创建SetupViewModel。相反,只需传入要与SetupView关联的SetupViewModel而不是传递给SetupViewModel构造函数的组成属性。所以代码变成......
public class SetupView
{
public static SetupView CreateView(SetupViewModel model)
{
var setupView = new SetupView(); { DataContext = model };
return setupView;
}
}
答案 6 :(得分:1)
为什么不使用简单构造函数的两个类?
public class SetupViewModel {
public SetupViewModel(DatabaseModel databaseModel, SettingsModel settingsModel, ViewUtilities viewUtilities) {
this.DatabaseModel = databaseModel;
this.SettingsModel = settingsModel;
this.ViewUtilities = viewUtilities;
}
}
和
public class SetupView {
public SetupView(SetupViewModel model) {
this.DataContext = model;
}
}
这样,两个类都清楚地说明了他们需要构建的内容。
答案 7 :(得分:0)
你可以称之为“巫师”。
答案 8 :(得分:0)
我倾向于同意Jason的观点。对我而言,它更像是一个Builder模式,但如果你将SetupView变成一个界面,它就更像是一个Factory模式。 Factory模式的关键是让Factory模式完成所有工作,决定从函数调用返回什么。所以我认为你必须在给定输入参数的各种选项之间选择一个开关。