就我个人而言,我从未理解工厂类的想法,因为直接实例化Object似乎更有用。我的问题很简单,在什么情况下使用工厂类模式是最佳选择,出于什么原因,以及良好的工厂类是什么样的?
答案 0 :(得分:51)
这是我的代码库中真正的实时工厂。它用于生成一个采样器类,它知道如何从一些数据集中采样数据(它最初是在C#中,所以请原谅任何java faux-pas)
class SamplerFactory
{
private static Hashtable<SamplingType, ISampler> samplers;
static
{
samplers = new Hashtable<SamplingType, ISampler>();
samplers.put(SamplingType.Scalar, new ScalarSampler());
samplers.put(SamplingType.Vector, new VectorSampler());
samplers.put(SamplingType.Array, new ArraySampler());
}
public static ISampler GetSampler(SamplingType samplingType)
{
if (!samplers.containsKey(samplingType))
throw new IllegalArgumentException("Invalid sampling type or sampler not initialized");
return samplers.get(samplingType);
}
}
以下是一个示例用法:
ISampler sampler = SamplerFactory.GetSampler(SamplingType.Array);
dataSet = sampler.Sample(dataSet);
如您所见,代码并不多,而且只需更短更快就可以了。
ArraySampler sampler = new ArraySampler();
dataSet = sampler.Sample(dataSet);
比使用工厂。那为什么我甚至打扰?嗯,有两个基本原因,相互依赖:
首先,它是代码的简单性和可维护性。假设在调用代码中,enum
作为参数提供。即如果我有一个需要处理数据的方法,包括采样,我可以写:
void ProcessData(Object dataSet, SamplingType sampling)
{
//do something with data
ISampler sampler = SamplerFactory.GetSampler(sampling);
dataSet= sampler.Sample(dataSet);
//do something other with data
}
而不是更麻烦的构造,如下所示:
void ProcessData(Object dataSet, SamplingType sampling)
{
//do something with data
ISampler sampler;
switch (sampling) {
case SamplingType.Scalar:
sampler= new ScalarSampler();
break;
case SamplingType.Vector:
sampler= new VectorSampler();
break;
case SamplingType.Array:
sampler= new ArraySampler();
break;
default:
throw new IllegalArgumentException("Invalid sampling type");
}
dataSet= sampler.Sample(dataSet);
//do something other with data
}
请注意,每次我需要一些采样时,都应该写出这种怪异。你可以想象,如果我在ScalarSampler
构造函数中添加了一个参数,或者添加了一个新的SamplingType
,那么更改会有多么有趣。现在这家工厂只有三个选项,想象一下有20个实施的工厂。
其次,它是代码的解耦。当我使用工厂时,调用代码不知道或不需要知道名为ArraySampler
的类甚至存在。甚至可以在运行时解决该类,并且呼叫站点将更加明智。因此,我可以随意更改ArraySampler
类,包括但不限于直接删除类,如果,例如,我决定ScalarSampler
也应该用于数组数据。我只需要更改行
samplers.put(SamplingType.Array, new ArraySampler());
到
samplers.put(SamplingType.Array, new ScalarSampler());
它会神奇地起作用。我不必在调用类中更改一行代码,这些代码可能有数百个。实际上,工厂使我能够控制采样的内容和方式,并且任何采样更改都可以有效地封装在与系统其余部分连接的单个工厂类中。
答案 1 :(得分:26)
这里的想法是关注点分离:如果使用该对象的代码也有足够的信息来实例化它,那么您就不需要工厂了。但是,如果涉及某些逻辑或配置,您不希望API用户考虑(或搞乱),您可以隐藏所有这些(并将其封装以便重复使用)。/ p>
以下是一个示例:您想要访问Google App Engine提供的其中一项服务。相同的代码应该在开发环境(其中有两个版本,主从和高可用性)和完全不同的本地开发环境中工作。谷歌不想告诉你他们内部基础设施的内部运作情况,你真的不想知道。所以他们所做的就是提供接口和工厂(以及工厂可以选择的那些接口的几种实现,你甚至不需要知道)。
答案 2 :(得分:4)
就个人而言,我在运行时未知接口的实现时使用工厂模式,或者可以使其动态化。
这意味着作为一名开发人员,我会使用对象实例的已知接口,但我并不关心实现的工作方式。
以p为例。您可以使用工厂模式为您提供数据库中的对象。您不关心该数据库是平面文件,本地/单用户数据库,服务器数据库还是Web资源,只是工厂可以生成和管理这些对象。我不想为每种情况编写实现:P
答案 3 :(得分:4)
来自Joshua Bloch的Effective Java一书,部分由我重写:
1)与构造函数不同,静态工厂方法( SFM )具有名称。
public static ComplexNumber one () {
return new ComplexNumber(1, 0);
}
public static ComplexNumber imgOne () {
return new ComplexNumber(0, 1);
}
public static ComplexNumber zero () {
return new ComplexNumber(0, 0);
}
2)每次调用SFM
时都不需要创建新对象
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
3)SFM
可以返回其返回类型的任何子类型的对象。
4)SFM
减少了创建参数化类型实例的详细程度。
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
Map<String, List<String>> m = HashMap.newInstance();
答案 4 :(得分:1)
为了补充Thilo的答案,让我们假设你有一个只有一个布尔值作为构造函数的对象:每次构建一个对象是完全浪费的,因为它只有两个可能的值。
在这种情况下,您可以创建静态工厂方法。 Java的Boolean
类就是一个例子:Boolean.valueOf()
。
答案 5 :(得分:0)
工厂本身并不那么容易显示它的美丽。当您看到真正的好处时将它与其他模式结合使用,例如,如果您想使用装饰器模式,直接实例化对象可能会为您的代码添加一些额外的耦合。正如OOP老师所说,耦合是坏的:)所以如果你要实例化装饰对象并且不想增加耦合,那么你可以使用工厂。
答案 6 :(得分:0)
您可以参考wikipedia,但大多数设计模式的基本思想是引入一些抽象来实现更好的可维护性和/或可重用性。工厂方法模式也不例外,它的作用是从代码中抽象出创建的复杂性。
对于简单的情况,似乎没有必要使用工厂模式,简单new
就足够了。但是当你需要更多的灵活性或功能时,这种模式可能有所帮助。
例如,除非需要新实例,否则静态工厂valueOf(boolean)
通常是比new Bealean(boolean)
更好的选择,因为它可以避免创建不必要的对象。工厂方法模式也称为虚拟构造函数。众所周知,多态性是OOP的关键特性之一,但构造函数不能是多态的,这个缺点可以通过工厂方法模式来克服。
本质上,直接实例化对象(通常通过new
)几乎不是具体的实现,而工厂方法模式屏蔽 volatile 实现 stable 接口(不限于Java中的interface
),在一些抽象背后推动对象创建的逻辑,以确保更易于维护和可重复使用的代码。
最后,要充分了解工厂方法模式和其他设计模式的好处,需要掌握OOP的本质,包括data abstraction,多态抽象和SOLID原则。