Published on

设计模式-工厂方法模式

Authors
  • avatar
    Name
    ReLive27
    Twitter

工厂方法模式

定义

工厂方法模式使用的频率非常高,在日常开发中经常会使用,其定义为:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

通用类图如下:

工厂方法模式中,抽象产品类定义产品的共性;Factory为抽象创建类,也就是抽象工厂。代码如下:

抽象产品类代码:

public abstract class Product {

    //产品类的公共方法
    public void method1(){
        //doSomething
    }

    public abstract void method2();
}

具体产品类可以有多个,都继承与抽象产品类,代码如下:

public class ConcreteProduct1  extends Product{
    @Override
    public void method2() {
        System.out.println("具体产品类1");
    }
}

public class ConcreteProduct2  extends Product{
    @Override
    public void method2() {
        System.out.println("具体产品类2");
    }
}

抽象工厂类负责定义产品对象产生,代码如下:

public interface ProductFactory {
		//创建一个产品类对象,参数可以自行设置
    <T extends Product> T createProduct(Class<T> clazz);
}

具体如何产生一个产品的对象,由具体的工厂类实现,代码如下:

public class ConcreteProductFactory implements ProductFactory {
    @Override
    public <T extends Product> T createProduct(Class<T> clazz) {
        Product product = null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) {
            //异常处理
        }
        return (T) product;
    }
}

工厂方法模式优点

  • 良好的封装性,代码结构清晰。对于客户端隐藏创建对象的复杂过程,降低模块间的耦合。
  • 扩展性高。在增加产品类的情况下,只要修改具体的工厂类或扩展一个工厂类。
  • 屏蔽产品类。产品类的实现如何变化,客户端都不需要关心,他只需要关心产品的接口,因为产品类的实例化由工厂类负责。
  • 工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他实现类都不关心,符合迪米特法则。只依赖产品类的抽象,符合依赖倒置原则。使用产品子类替换产品父类,也符合里氏替换原则。

使用场景

工厂方法模式是new一个对象的替代品,在需要灵活的,可扩展的框架时,可以考虑采用工厂方法模式。例如使用JDBC数据库连接,数据库从MySQL切换到Oracle,需要改动的地方就是切换下驱动名称(前提SQL是标准语句)。

工厂方法模式的扩展

简单工厂模式

在工厂方法模式中,我们考虑一个模块只需要一个工厂类,根据这一需求,稍微改动下,将抽象工厂类去掉,类图如下:

我们去掉Factory抽象类,并把创建createProduct方法设置为静态类型,变更代码如下:

public class ConcreteProductFactory {
    @Override
    public <T extends Product> T createProduct(Class<T> clazz) {
        Product product = null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) {
            //异常处理
        }
        return (T) product;
    }
}

简单工厂模式是工厂模式的弱化,在实际开发中,采用此模式还是比较多的,但是其缺点是工厂类的扩展比较困难,不符合开闭原则。

多工厂类

当我们做一个复杂项目时,如果遇到一个对象初始化很耗费精力的情况,那么将所有产品类放到一个工厂方法中会使结构不清晰,例如产品类有5个具体实现,那么此时工厂方法可能会变得巨大无比。

考虑需要结构清晰,我们就为每个产品类创造一个工厂类,由客户端决定与哪个工厂方法关联。

多工厂模式的抽象工厂类代码如下:

public interface MultiProductFactory {

    Product createProduct();
}

此时抽象方法不需要传递参数,因为每个具体工厂职责已经很明确了,只需要负责创建自己负责的产品类对象。

产品类1工厂类实现:

public class Concrete1MultiProductFactory implements MultiProductFactory{
    @Override
    public Product createProduct() {
        return new ConcreteProduct1();
    }
}

产品类2工程类实现:

public class Concrete2MultiProductFactory implements MultiProductFactory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct2();
    }
}

此种模式就是创建类职责清晰,但是给扩展性和维护性带来了一定的影响。如果扩展一个产品类,就需要建立相应的工厂类,这样增加了扩展的难度。因为工厂类和产品类数量相同,维护时需要考虑两个对象之间的关系。当然在采用多工厂时,可以增加一个协调类,避免客户端与各个子工厂交流,协调类的作用封装子工厂类,对外提供统一的访问接口。

延迟初始化

一个对象消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被调用。延迟初始化是工厂方法模式的一个扩展应用。

工厂类负责对象的创建工作,并通过HashMap产生一个缓存,对需要再次被调用的对象保留,参考代码如下:

public class LazyProductFactory {
    private static final Map<String, Product> map = new HashMap<>();

    public static synchronized Product createProduct(String type) {
        Product product;
        if (map.containsKey(type)) {
            product = map.get(type);
        } else {
            if ("product1".equals(type)) {
                product = new ConcreteProduct1();
            } else {
                product = new ConcreteProduct2();
            }
            map.put(type, product);
        }
        return product;
    }
}

实现比较简单,通过定义一个Map容器,缓存所有产生的对象,再次调用时如果Map中已经包含则直接返回,否则根据需要产生一个对象并存Map中。

结论

与往常一样,本文中使用的源代码可在 GitHub 上获得。