工厂类[关闭]
Posted
技术标签:
【中文标题】工厂类[关闭]【英文标题】:Factory classes [closed] 【发布时间】:2013-01-12 14:00:39 【问题描述】:就我个人而言,我从来没有理解工厂类的概念,因为直接实例化一个 Object 似乎更有用。我的问题很简单,在什么情况下使用工厂类模式是最佳选择,出于什么原因,一个好的工厂类是什么样的?
【问题讨论】:
当您不确切知道将返回什么类时,工厂方法最有用。例如,您可以根据访问的资源是本地资源还是远程资源来获取不同的类。另一种情况是返回的对象是“复合”。例如,我希望 Java 有一些“给我一个文件访问权限”的方法,这些方法可以将文件、流和缓冲区粘在一起,令人作呕,而不是我不得不把这些东西放在一起。 【参考方案1】:来自 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();
【讨论】:
【参考方案2】:这是我的代码库中的一个真实的工厂。它用于生成一个采样器类,该类知道如何从某个数据集中采样数据(它最初是在 C# 中,所以请原谅任何 java 错误)
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
作为参数提供。 IE。如果我有需要处理数据的方法,包括采样,我可以写:
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 感谢您提供的真实示例。从“非书”示例中理解这将非常有帮助。【参考方案3】:您可以参考wikipedia,但大多数设计模式的基本思想是引入一些抽象以实现更好的可维护性和/或可重用性。工厂方法模式也不例外,它所做的就是从代码中抽象出创建的复杂性。
对于简单的情况,似乎没有必要使用工厂模式,一个简单的new
就足够了。但是当您需要更多的灵活性或功能时,这种模式可能会有所帮助。
例如,除非需要新实例,否则静态工厂valueOf(boolean)
通常比new Bealean(boolean)
更好,因为它避免了创建不必要的对象。工厂方法模式也称为Virtual Constructor。众所周知,多态是面向对象编程的关键特性之一,但构造函数不能是多态的,这个缺点可以通过工厂方法模式来克服。
本质上,直接实例化一个对象(通常通过new
)几乎不是一个具体的实现,而工厂方法模式屏蔽了一个volatile 实现 通过一个stable interface(不限于Java中的interface
),将创建对象的逻辑推到一些抽象后面,以确保代码更易于维护和重用。
归根结底,要充分理解工厂方法模式和其他设计模式的好处,需要掌握OOP的精髓,包括data abstraction、多态抽象和SOLID原理。
【讨论】:
【参考方案4】:工厂本身并不那么容易展现其美丽。当您将它与其他模式结合时,您会看到真正的好处,例如,如果您想使用装饰器模式,直接实例化一个对象可能会为您的代码添加一些额外的耦合。正如 OOP 老师所说,耦合很糟糕 :) 所以如果您要实例化装饰对象并且不想增加耦合,那么您可以使用工厂。
【讨论】:
【参考方案5】:为了补充 Thilo 的回答,让我们假设您有一个对象,它只有一个布尔值作为构造函数:每次都构建一个完全是浪费,因为它只有两个可能的值。
在这种情况下,您可以创建静态工厂方法。 Java 的Boolean
类就是一个例子:Boolean.valueOf()
。
【讨论】:
不过,这更像是一个“multiton”/flyweight 模式示例。【参考方案6】:就我个人而言,当接口的实现在运行时未知或者可以动态化时,我会使用工厂模式。
这意味着,作为开发人员,我使用对象实例的已知接口,但我并不关心实现的工作方式。
举个例子。您可以使用工厂模式为您提供数据库中的对象。您不关心该数据库是平面文件、本地/单用户数据库、服务器数据库还是 Web 资源,只要工厂可以生成和管理这些对象。
我不想为每个案例编写实现:P
【讨论】:
你的 Take-example 不是更像建造者模式吗? @DanielK factory 和 builder 类似,builder 获取属性并配置对象, factory 一般返回实现给定接口的对象【参考方案7】:这里的想法是关注点分离:如果使用对象的代码也有足够的信息来实例化它,那么您就不需要工厂。但是,如果您不想让 API 用户考虑(或弄乱)涉及的某些逻辑或配置,则可以将所有这些隐藏(并将其封装以供重用)在工厂中。
这是一个示例:您想要访问 Google App Engine 提供的一项服务。相同的代码应该在开发环境(有两个版本,主从和高可用)和完全不同的本地开发环境下都可以工作。谷歌不想告诉你他们内部基础设施的内部运作,你也不想知道。所以他们所做的是提供接口和工厂(以及这些接口的几个实现供工厂选择,您甚至不需要知道)。
【讨论】:
以上是关于工厂类[关闭]的主要内容,如果未能解决你的问题,请参考以下文章