定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类
- 抽象产品类Product:定义产品的共性,实现对事物的最抽象的定义
- 抽象创造类Creator:抽象工厂
- ConcreteCreator:具体创建产品类
通用代码
- 抽象产品类
public abstract class Product {
// 产品类的公共方法
public void method1() {
// 业务处理逻辑
}
// 抽象方法
public abstract void method2() {
}
}
- 具体产品类
public class ConcreteProduct1 extends Product {
public void method2() {
// 业务处理逻辑
}
}
public class ConcreteProduct2 extends Product {
public void method2() {
// 业务处理逻辑
}
}
- 抽象工厂类
public abstract class Creator {
/*
* 创建一个产品对象,其输入的参数可以是String, Enum Class等
*/
public abstract <T extends Product> T createProduct(Class<T> c);
}
- 具体工厂类
public class ConcreteCreator extends Creator {
public <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try {
product = (Product)Class.forName(c.getName()).newInstance();
} catch(Exception e) {
// 异常处理
}
return (T)product;
}
}
- 场景类
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
Product product = creator.createProduct(ConcreteProduct1.class);
/*
* 继续业务逻辑
*/
}
}
优点
- 良好的封装性,代码结构清晰
- 扩展性非常优秀。在增加产品类时,只需要修改具体的工厂类或扩展一个工厂类就可以完成。
- 屏蔽产品类,调用陈不需要关心产品类的具体实现。只要接口不变,上层模块就不需要改变。
使用场景
- 需要灵活,可扩展的框架时,可以考虑用工厂方法模式。
- 比如要设计一个连接邮件服务器的架构,有三种网络协议可供选择,POP3 IMAP HTTP,可以把这三种连接方法作为产品类,定义一个接口IConnectMail。再定义一个工厂,按照传入的条件不同,选择不同的连接方法。当某些邮件服务器提供WebService接口时,只需要增加一个产品类就可以完成改变。
- 可以用在异构项目中
扩展
缩小为简单工厂模式
一个模块只需要一个工厂,那么可以去掉抽象工厂,改为使用静态方法生产,这里关键是使用静态方法,这样不需要实例化工厂
升级为多个工厂类
当初始化一个对象很耗费精力时,所有产品类都放到一个工厂方法中进行初始化会使得代码结构不清晰,可以给每个产品定义一个创造者。- 延迟初始化
一个对象被消费完,不立刻释放,工厂类保持其初始状态,等待下次被使用。ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留。
// 实例代码 延迟加载的工厂类
public class ProductFactory {
private static final Map<String, Product> prMap = new HashMap();
public static synchronized product createProduct(String type) throw Exception {
Product product = null;
if (prMap.containsKey(type)) {
product = prMap.get(type);
} else {
if (type.equals("Product1")) {
product = new ConcreteProduct1();
} else {
product = new ConcreteProduct2();
}
prMap.put(type, product);
}
return product;
}
}
可以用来限制一个产品类的最大实例化数量,例如JDBC连接数据库,都要设置一个最大连接数,该数量就是内存中最大实例化的数量。
延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,可以通过延迟加载降低对象的产生和销毁带来的复杂性。
参考
《设计模式之禅》