什么是工厂方法模式?
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
工厂方法使一个类的实例化延迟到其子类。
通用类图如下:
工厂方法模式通用代码:
①产品抽象类:负责定义产品共性
public abstract class Product {
//产品的公共方法
public void method1(){
//业务逻辑处理代码
}
//其他抽象方法
public abstract void method2();
}
具体的产品实现类可以有多个,都继承于抽象产品类
②产品实现类
public class ConcreteProduct1 extends Product { //具体产品实现类1
@Override
public void method2() {
//业务逻辑处理代码
}
}
public class ConcreteProduct2 extends Product { //具体产品实现类2
@Override
public void method2() {
//业务逻辑处理代码
}
}
③抽象工厂类:负责定义产品对象的产生
public abstract class AbstractFactory {
/**
* 创建一个产品对象,其输入参数可以自行设置
* 输入参数:通常为String,Enum,Class,也可以为空
*/
public abstract <T extends Product>T createProduct(Class<T> c);
}
④具体的工厂实现类:定义具体如何产生一个产品对象
public class ConcreteFactory extends AbstractFactory {
@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;
}
}
⑤场景类:调用相关方法
public class Client {
public static void main(String[] args) {
AbstractFactory factory = new ConcreteFactory();
Product product = factory.createProduct(ConcretepProduct1.class);
}
}
工厂方法模式的优点:
①良好的封装性,代码结构清晰
②优秀的扩展性
③屏蔽产品类,很强的灵活性
④解耦性强
工厂方法模式的使用场景:
①工厂方法模式是new方法的替代品,在所有需要生成对象的地方都可以使用,但需要谨慎的考虑是否要增加一个工厂类进行管理,防止增加代码复杂度。
②需要灵活的,可扩展的框架时,可以考虑采用工厂方法模式。
③可以用在异构的项目中。
④可以在测试驱动开发的框架下使用。
工厂方法模式的扩展
①缩小为简单工厂模式
问题:一个模块仅需要一个工厂类,没必要把它生产出来,使用静态方法即可。
解决思路:以上面代码为例,去掉AbstractFactory抽象类,同时将createProduct方法设置为静态类型,简化类的创建过程。
那么ConcreteFactory代码修改如下:
public class ConcreteFactory{
public static <T extends Human> 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;
}
}
客户端类修改如下:
public class Client {
public static void main(String[] args) {
Product product =ConcreteFactory.createProduct(ConcretepProduct1.class);
}
}
运行结果没有变化,但是调用者变简单了,该模式是工厂方法模式的简化版,所以称之为:简单工厂模式,也叫静态工厂模式。缺点是:扩展困难,不符合开闭原则。
②升级为多个工厂类
问题:在一个复杂的项目中,将所有产品类放到一个工厂方法中进行初始化会使代码结构不清晰。
解决思路:为每一个产品定义一个创造者,然后由调用者自己选择与哪个工厂方法关联。
AbstractFactory抽象类代码修改如下:
public abstract class AbstractFactory {
public abstract Product createProduct();
}
抽象方法中已不需要传递参数,每个具体的工厂都已经非常明确各自的职责。
public class Product1Factory extends AbstractFactory {
public Product createProduct(){
return new ConcreteProduct1();
}
}
public class Product2Factory extends AbstractFactory {
public Product createProduct(){
return new ConcreteProduct2();
}
}
客户端类修改如下:
public class Client {
public static void main(String[] args) {
Product product1 = (new Product1Factory()).createProduct();
Product product2 = (new Product2Factory()).createProduct();
}
}
回顾一下:为每一个产品实现类都创建一个具体的工厂类,职责清晰,结构简单,缺点是:可扩展性与可维护性带来困难。
③替代单例模式
单例模式的核心在于:内存中只能有一个对象,那么可以使用工厂方法模式也只创建一个对象么?答案是肯定的。
首先创建一个Singleton类:
public class Singleton {
private Singleton(){}
public void doSomething(){}
}
将构造方法私有化,这样就无法通过正常方式构造一个Singleton对象,那么SingletonFactory应该如何创建一个单例对象呢?答案是:通过反射。
public class SingletonFactory {
private static Singleton single;
static {
try {
Class c = Class.forName(Singleton.class.getName());
//获取无参构造器
Constructor cs = c.getDeclaredConstructor();
//设置无参构造器是可以访问的
cs.setAccessible(true);
//创建一个实例
single = (Singleton)cs.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//获取单例对象的外部访问方法
public static Singleton getSingleton(){
return single;
}
}
在这里主要注意单例模式下的反射影响。
④延迟初始化:一个对象被消费完毕后,并不立即释放,工厂类保持其初始状态,等待再次被使用。
代码重现:
public class ProductFactory {
private static final Map<String,Product>PRODUCT_MAP = new HashMap<>();
public static synchronized Product createProduct(String type)throws Exception{
Product product = null;
if (PRODUCT_MAP.containsKey(type)){
product = PRODUCT_MAP.get(type);
}else {
if (Objects.equals(type,"Product1")){
product = new ConcreteProduct1();
}else {
product = new ConcreteProduct2();
}
PRODUCT_MAP.put(type,product);
}
return product;
}
}
ProductFactory 负责产品类对象的创建工作,并且通过 PRODUCT_MAP 变量产生一个缓存,将需要再次被使用的对象保留。
延迟初始化框架是可以扩展的,比如限制产品的最大数量,可以通过判断map集合的大小来实现;比如JDBC连接数据库,判断最大连接数MaxConnections等
参考书籍:设计模式之禅 --- 秦小波 著