将多个类对象交给工厂类来生成的设计方式被称为工厂模式,工厂类可以让系统具有更好的可维护性、可扩展性。
一、简单工厂模式实现
1. 定义一个 Computer 类,调用 Output 对象,将 Computer 类与Output 的实现类分离开来,只需面向Output接口编程即可。
package see;
public class Computer {
private Output out;
public Computer(Output out) {
this.out = out;
}
// 定义一个模拟获取字符串输入的方法
public void keyIn(String msg) {
out.getData(msg);
}
// 定义一个模拟打印的方法
public void print() {
out.out();
}
public static void main(String[] args) {
// 创建OutputFactory
OutputFactory of = new OutputFactory();
// 将Output对象传入,创建Computer对象
Computer c = new Computer(of.getOutput());
c.keyIn("轻量级Java EE企业应用实战");
c.keyIn("疯狂Java讲义");
c.print();
}
}
2. 定义一个 OutputFactory 工厂类,负责产生Output实现类的实例。
package see;
public class OutputFactory {
public Output getOutput() {
// 下面两行代码用于控制系统到底使用Output的哪个实现类。
// return new Printer();
return new BetterPrinter();
}
}
3. Output接口
package see;
public interface Output {
// 接口里定义的属性只能是常量
int MAX_CACHE_LINE = 50;
// 接口里定义的只能是public的抽象实例方法
void out();
void getData(String msg);
}
4. Output实现类一Printer
package see;
//让Printer类实现Output
public class Printer implements Output {
private String[] printData = new String[MAX_CACHE_LINE];
// 用以记录当前需打印的作业数
private int dataNum = 0;
public void out() {
// 只要还有作业,继续打印
while (dataNum > 0) {
System.out.println("打印机打印:" + printData[0]);
// 把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
public void getData(String msg) {
if (dataNum >= MAX_CACHE_LINE) {
System.out.println("输出队列已满,添加失败");
} else {
// 把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++] = msg;
}
}
}
5. Output实现类二BetterPrinter
package see;
public class BetterPrinter implements Output {
private String[] printData = new String[MAX_CACHE_LINE * 2];
// 用以记录当前需打印的作业数
private int dataNum = 0;
public void out() {
// 只要还有作业,继续打印
while (dataNum > 0) {
System.out.println("高速打印机正在打印:" + printData[0]);
// 把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
public void getData(String msg) {
if (dataNum >= MAX_CACHE_LINE * 2) {
System.out.println("输出队列已满,添加失败");
} else {
// 把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++] = msg;
}
}
}
二、类似Spring IoC容器的工厂模式实现
1. 定义一个类似与Spring配置文件的配置文件bean.xml
<?xml version="1.0" encoding="GBK"?>
<beans>
<bean id="computer" class="test.Computer">
<!-- 为out属性注入普通属性值 -->
<property name="out" ref="betterPrinter"/>
</bean>
<!-- 配置两个Bean实例 -->
<bean id="printer" class="test.Printer"/>
<bean id="betterPrinter" class="test.BetterPrinter"/>
<!-- 配置一个prototype行为的Bean实例 -->
<bean id="now" class="java.util.Date" scope="prototype"/> <!--①-->
</beans>
2. 定义工厂类(需引入dom4j-1.6.1.jar解析配置文件)
接口ApplicationContext:
package test;
public interface ApplicationContext {
// 获取指定Bean实例的方法
Object getBean(String name) throws Exception;
}
实现类YeekuXmlApplicationContext:
package test;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;
import org.dom4j.*;
import org.dom4j.io.*;
public class YeekuXmlApplicationContext implements ApplicationContext {
// 保存容器中所有单例模式的Bean实例
private Map<String, Object> objPool = Collections.synchronizedMap(new HashMap<String, Object>());
// 保存配置文件对应的Document对象
private Document doc;
// 保存配置文件里的根元素
private Element root;
public YeekuXmlApplicationContext(String filePath) throws Exception {
SAXReader reader = new SAXReader();
doc = reader.read(new File(filePath));
root = doc.getRootElement();
initPool();
initProp();
}
public Object getBean(String name) throws Exception {
Object target = objPool.get(name);
// 对于singleton Bean,容器已经初始化了所有Bean实例
if (target.getClass() != String.class) {
return target;
} else {
String clazz = (String) target;
// 对于prototype并未注入属性值
return Class.forName(clazz).newInstance();
}
}
// 初始化容器中所有singleton Bean
private void initPool() throws Exception {
// 遍历配置文件里的每个<bean.../>元素
for (Object obj : root.elements()) {
Element beanEle = (Element) obj;
// 取得<bean.../>元素的id属性
String beanId = beanEle.attributeValue("id");
// 取得<bean.../>元素的class属性
String beanClazz = beanEle.attributeValue("class");
// 取得<bean.../>元素的scope属性
String beanScope = beanEle.attributeValue("scope");
// 如果<bean.../>元素的scope属性不存在,或为singleton
if (beanScope == null || beanScope.equals("singleton")) {
// 以默认构造器创建Bean实例,并将其放入objPool中
objPool.put(beanId, Class.forName(beanClazz).newInstance());
} else {
// 对于非singlton Bean,存放该Bean实现类的类名。
objPool.put(beanId, beanClazz);
}
}
}
// 初始化容器中singleton Bean的属性
private void initProp() throws Exception {
// 遍历配置文件里的每个<bean.../>元素
for (Object obj : root.elements()) {
Element beanEle = (Element) obj;
// 取得<bean.../>元素的id属性
String beanId = beanEle.attributeValue("id");
// 取得<bean.../>元素的scope属性
String beanScope = beanEle.attributeValue("scope");
// 如果<bean.../>元素的scope属性不存在,或为singleton
if (beanScope == null || beanScope.equals("singleton")) {
// 取出objPool的指定的Bean实例
Object bean = objPool.get(beanId);
// 遍历<bean.../>元素的每个<property.../>子元素
for (Object prop : beanEle.elements()) {
Element propEle = (Element) prop;
// 取得<property.../>元素的name属性
String propName = propEle.attributeValue("name");
// 取得<property.../>元素的value属性
String propValue = propEle.attributeValue("value");
// 取得<property.../>元素的ref属性
String propRef = propEle.attributeValue("ref");
// 将属性名的首字母大写
String propNameCamelize = propName.substring(0, 1).toUpperCase()
+ propName.substring(1, propName.length());
// 如果<property.../>元素的value属性值存在
if (propValue != null && propValue.length() > 0) {
// 获取设值注入所需的setter方法
Method setter = bean.getClass().getMethod("set" + propNameCamelize, String.class);
// 执行setter注入
setter.invoke(bean, propValue);
}
if (propRef != null && propRef.length() > 0) {
// 取得需要被依赖注入的Bean实例
Object target = objPool.get(propRef);
// objPool池中不存在指定Bean实例
if (target == null) {
// 此处还应处理Singleton Bean依赖prototype Bean的情形
}
// 定义设值注入所需的setter方法
Method setter = null;
// 遍历target对象所所实现的所有接口
for (Class superInterface : target.getClass().getInterfaces()) {
try {
// 获取设值注入所需的setter方法
setter = bean.getClass().getMethod("set" + propNameCamelize,
superInterface);
// 如果成功取得该接口对应的方法,直接跳出循环
break;
} catch (NoSuchMethodException ex) {
// 如果没有找到对应的setter方法,继续下次循环
continue;
}
}
// 如果setter方法依然为null,
// 则直接取得target实现类对应的setter方法
if (setter == null) {
setter = bean.getClass().getMethod("set" + propNameCamelize,target.getClass());
}
// 执行setter注入
setter.invoke(bean, target);
}
}
}
}
}
}
3. 测试工厂类,其中Computer类、Output接口、Printer类、BetterPrinter类和上面相同
package test;
public class IoCTest {
public static void main(String[] args) throws Exception {
// 创建IoC容器
ApplicationContext ctx = new YeekuXmlApplicationContext("bean.xml");
// 从IoC容器中取出computer Bean
Computer c = (Computer) ctx.getBean("computer");
// 测试Computer对象
c.keyIn("轻量级Java EE企业应用实战");
c.keyIn("疯狂Java讲义");
c.print();
System.out.println(ctx.getBean("now"));
}
}