设计模式之禅——工厂方法模式

引:在面向对象的思维中,万物皆对象,就像有女蜗造人,我们也可以用工厂方法模式造对象。

定义

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

工厂方法模式的通用类图如下:
FactoryMethod

下面是一个比较实用的通用源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 抽象产品类
public abstract class Product {
// 产品类的公共方法
public void method1() {
// 业务逻辑处理
}
//抽象方法
public abstract void method2();
}

// 具体产品类
public class ContreteProduct1 extends Product {
@Override
public void method2() {
// 业务逻辑处理
}
}
public class ContreteProduct2 extends Product {
@Override
public void method2() {
// 业务逻辑处理
}
}

// 抽象工厂类
public abstract class Creator {
/*
* 创建一个产品对象,其输入参数类型可以自行设置
* 通常为String, Enum, Class等,当然也可以为空
*/

public abstract <T extends Product> T createProduct(Class<T> c);
}

// 具体工厂类
public class ContreteCreator extends Creator {
public abstract <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 ContreteCreate();
Product product = creator.createProduct(ContreteProduct1.class);
/*
* 继续业务处理
*/
}
}

应用

优点

  1. 良好的封装性,代码结构清晰。
  2. 扩展性非常优秀,要增加一个新的产品,只要实现Product接口。
  3. 屏闭产品类。只要关心产品的接口即可,例如换数据库只要换驱动即可。
  4. 符合迪米特法则(只要知道产品的接口即可);符合依赖倒置原则(值依赖产品的抽象类即可);符合里氏替换原则(使用产品子类可以替换产品父类)。

使用场景

  1. 只要使用new的地方都可以使用工厂方法模式,但是要考虑代码的复杂度。
  2. 需要灵活的,可扩展的框架时(有多个产品可选且可以随时增加时),可以考虑工厂方法模式。
  3. 工厂方法模式可以用在异构项目中。
  4. 可以使用在测试驱动开发的框架下。

工厂方法模式的扩展

  1. 缩小为简单工厂模式

    实质是去掉了创造者接口,使具体创造者直接依赖产品接口。缺点是扩展比较困难,不符合开闭原则。

  2. 升级为多个工厂类

    实质是为了结构清晰,我们为每个产品定义一个创造者,然后由调用者自己去选择与那个工厂方法关联。

  3. 替代单例模式

    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class SingletonFactory {
    private static Singleton singleton;
    static {
    try {
    Class c1 = Class.forName(Singleton.class.getName());
    // 获得无参构造
    Constructor constructor = c1.getDeclaredConstructor();
    //设置无参构造是可访问的
    constructor.setAccessible(true);
    // 产生一个实例对象
    singleton = (Singleton) constructor.newInstance();

    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    public static Singleton getSingleton() {
    return singleton;
    }
    }
  4. 延迟初始化

    所谓延迟初始化即一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。其实质,是利用一个Map保存创造过的对象,如果在Map容器已经有的对象,则直接取出返回;如果没有,则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用。

最佳实践

孰能生巧,熟练掌握该设计模式,多思考工厂方法如和应用,而且工厂方法模式可以与其他模式混合使用,变化出无穷的优秀设计。

参考

  1. 《设计模式之禅》