【设计模式】简单工厂
简单工厂模式
严格的说,简单工厂模式并不属于 23 中设计模式,不过我浏览了几本与设计模式相关的术后,发现都提及了该知识点,可能作者的目的是为了让我们能循序渐进地理解后面的工厂方法、抽象工厂。总之,这个模式还是一个很值得去理解的模式。
问题的引入
在设计原则中有这么一句话——“要面向接口编程,而不要面向实现编程”。不过我们一直使用 new
关键字创建对象,这好像有悖这一原则。
接口的思想是封装隔离,这里的封装指的是“对被隔离体的行为的封装”,或者说是“对被隔离体职责的封装”;所以面向接口编程,可以免去将来系统发生的一系列修改。假如是面向接口编程,那么今后代码发生改变时,可以通过多态,让任何新的类实现该接口。但是如果我们在代码中将接口引向具体类,那么情况就不是那么乐观了,因为一旦要加入新的具体类,你就必须改变代码。用代码来说明或许会简明了当些
首先我们定义一个接口
public interface Utils {
/**
* 定义一个行为
*/
void log(String s);
}
现在我们再定义一个类来实现这个接口
public class StringUtils implement Utils {
public void log(String s) {
sout("StringUtils Implement:" + s);
}
}
那么此时我们会怎么调用这个接口中的行为呢?使用接口的实现类?
public class Test {
public static void main(String[] args) {
Utils utils = new StringUtils();
utils.log("hello world!");
}
}
这样看起来这段代码好像没什么问题。但是,接口的思想是“封装隔离”,所以实现类 StringUtils 应该是被接口 Utils 封装并同 Test 离开的,换而言之就是 Test 类不应该知道 Utils 的实现类是 StringUtils。以上代码并不符合DIP(Dependence Inversion Principle)原则,所以在这里,我们就需要有一个简单的方法,当我传递参数调用它的时候,就可以返回一个我要的具体实现对象给我。这就是将要介绍的简单工厂模式。
实现思路
简单工厂的定义:
提供一个创建对象实例的功能,而无需关心其具体实现。被创建实例的类型可以是接口、抽象类、也可以是具体的类。
现在在之前代码的基础上,对 Utils 新增加一个实现类
public class LogUtils implement Utils {
public void log(String s) {
sout("LogUtils Implement:" + s);
}
}
接下来就可以创建一个简单工厂了,实例代码如下:
public class Factory {
// 具体创建 Utils 对象的方法
public static Utils createUtils(String condition) {
Utils utils = null;
if (condition.equals("string")) {
utils = new StringUtils();
}
else if(condition.equals("log")) {
utils = new LogUtils();
}
return utils;
}
}
现在我们就可以使简单工厂方法来得到一个对象了
public class Test {
public static void main(String[] args) {
Utils utils = Factory.createUtils("String");
utils.log("hello java");
}
}
可配置的简单工厂
上面代码就是简单工厂的具体实现了,也达到了我们想要的封装和解耦的目的,但是还有一个问题,就是要怎么样添加一个新的实现类呢?假如我现在对 Utils 接口还有一个新的实现类 DateUtils,那么要怎么样将其添加到工厂中呢?
最简单的方式当然是在工厂类的方法中添加新的代码块了
else if(confition.equals("date") {
utils = new DateUtils();
}
不过每增加一个新的实现类,就改动一次工厂类,这样的实现方式不是很妥当,所以就要引入其他的方式了,例如通过配置文件来实现添加新的实现类
首先你需要定义一个配置文件“Factory.properties”,然后在配置类中定义你要的实现类
ImplClass=com.java.factory.DateUtils
如果添加了实现类,修改配置文件中的配置即可,就不用去修改代码而从新编译了。
接下来定义新的工厂类的实现
public class Factory() {
public static Utils createUtils() throws Exception {
Properties p = new Properties();
InputStream in = Factory.class.getResourceAsStream("Factory.properties");
p.load(in);
Utils utils = (Utils)Class.forName(p.getProperty("ImplClass").newInstance())
return utils;
}
}
emmmm,这样实现应该可以解决上面的问题了。不过这里还有更好的实现方式,就是采用Ioc/DI的方式来实现。
谈一谈优劣
- 优点:简单工厂帮助实现了封装隔离,使外部调用组件能真正实现面向接口编程,同时也实现了程序间的解耦。
- 劣势:加重了程序员理解代码的成本,如果工厂需要通过参数来选择实现类,那么就还需要让程序员去理解每个参数所代表的含义。