(一)什么是工厂模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。其实我觉得工厂方法就是将创建对象的方法(如new、反射)封装在一个专门的方法里,怎么创建对调用者来说是透明的
(二)一个比较经典的工厂方法模式
抽象工厂类:Creator.java
public abstract class Creator {
public abstract <T extends Product> T createProduct(Class<T> c) ;
}
这里还使用了泛型
抽象工厂实现类: ConcreteCreator.java
public class ConcreteCreator extends Creator {
@Override
public <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try {
product = (Product) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T)product;
}
}
产品抽象类:Product .java
public abstract class Product {
public void method1() {
//业务处理逻辑
}
public abstract void method2();
}
产品实现类一:ConcreteProduct1.java
public class ConcreteProduct1 extends Product {
@Override
public void method2() {
//业务逻辑处理
}
}
产品实现类二:ConcreteProduct2.java
public class ConcreteProduct2 extends Product {
@Override
public void method2() {
//业务逻辑处理
}
}
这里在补充一下什么是工厂方法模式。前面提到用一个方法封装创建对象的方法就是工厂方法模式,然而
似乎并不是简单的代替,需要有抽象类,对一类事物的共性进行抽象,当然,抽象类可以使用接口来代替
(三)工厂方法模式的优点
1)方便扩展,比如你想多生产一种不同的产品,只需增加一个类就可以了,而工厂类完全不需要进行修改
2)屏蔽产品类
3)解耦框架
(四)工厂方法模式的应用场景
1)使用new创建对象的地方。但是我觉得这种应用场景还是使用new吧,反射那么难,如果不是调用默认构造函数,语法还是有点多的,代码就不够简洁
2)需要灵活、可扩展的框架
spring中的ioc应该可以算是吧
3)测试驱动开发框架下。不过这种场景也一般不使用了,测试可以使用JMock、EasyMock、Mockmvc
(五)工厂方法模式的扩展
上面一个例子是一个比较通用的例子,工厂方法模式还有很多的扩展
1)缩小为简单工厂模式
就是不需要抽象类
对上述的例子进行修改,不需要Creator抽象类,只有ConcreteCreator.java
public class ConcreteCreator {
@Override
public static <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try {
product = (Product) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T)product;
}
}
并且将方法修改为static,简化了方法调用,直接类名.方法名就搞定了
缺点:扩展性差
优点:简单
2)升级为多个工厂
应用场景:当所有的产品类都放到一个工厂方法中进行初始化时会造成代码结构不清晰时,每个产品对应一个工厂类,此时采用new替换反射创建对象
3)替代单例模式
Singleton.java
public class Singleton {
//不允许通过new产生一个对象
private Singleton() {}
public void doSomething() {
//业务处理
}
}
构造函数为private,则该方法不能被外界调用,就不能通过正常方式(new)来创建对象了
SingletonFactory.java
public class SingletonFactory {
private static Singleton singleton;
static {
try {
Class c1 = Class.forName(Singleton.class.getName());
//获得无参构造函数
try {
Constructor constructor = c1.getDeclaredConstructor();
//设置无参构造函数可以访问
constructor.setAccessible(true);
//产生一个实例对象
singleton = (Singleton) constructor.newInstance();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
这里之所以能保证只能初始化一次是因为static代码块在类初始化时进行加载,并且只初始化一次
4)延迟初始化
先解释一下概念延迟初始化:一个对象被消费完毕以后,并不立刻释放,等待再次被使用。原来概念是这样的,不能按照字面来理解,原来还以为等要使用时在进行初始化呢(这应该是懒加载吧)
延迟初始化是工厂方法模式的一个扩展应用
ProductFactory.java
public class ProductFactory {
private static final Map<String, Product> map = new HashMap<>();
public static synchronized Product createProduct(String type) {
Product product = null;
if (map.containsKey(type)) {
product = map.get(type);
} else {
if (type.equals("Product1")) {
product = new ConcreteProduct1();
} else {
product = new ConcreteProduct2();
}
map.put(type, product);
}
return product;
}
}
其实就是用map模拟了一个缓存的效果,将创建的对象放在map中,每次创建时会先检查要创建的对象是否在map中,如果在则使用map中的对象,否则创建一个,并将其放至map中
synchronized保证线程安全性
延迟初始化可以用在对象初始化比较复杂的情况下,可以通过延迟加载降低对象的产生和销毁带来的复杂性和开销
(六)小结
工厂方法模式就是使用封装创建对象的方法,在这个基础上使用继承和接口,方便程序的扩展。工厂方法模式有很多的扩展,在不同场景选择不同的扩展方式