1 概述
创建型模式,提供了一种创建对象的最佳实践。工厂方法模式(Factory Method Pattern)的核心思想,是通过统一的工厂类来获取对象,而不需要调用类的构造方法。
2 优点
- 可以将类的实例化过程延缓到子类。调用者无需知道接口/抽象类的具体实现是什么,利用工厂方法即可获取类的实例,降低与调用者的耦合度。
- 隐藏类的构造细节,降低类创建的复杂度,提高程序可读性。
- 可以根据不同环境/参数,从工厂构造不同的方法。
3 案例
有一个饭店的接口,饭店里有厨师和服务员。我们定义KFC
和PizzaHut
两个饭店:
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
interface Restaurant {
void getCook();
void getWaiter();
}
public class KFC implements Restaurant {
@Override
public void getCook() {
System.out.println("I'm KFC cook.");
}
@Override
public void getWaiter() {
System.out.println("I'm KFC waiter.");
}
}
class PizzaHut implements Restaurant {
@Override
public void getCook() {
System.out.println("I'm PizzaHut cook.");
}
@Override
public void getWaiter() {
System.out.println("I'm PizzaHut waiter.");
}
}
3.1 工厂类型1
再定义一个工厂方法RestaurantFactory
,从工厂中,很容易就能根据类型获取对应的饭店:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test {
public static void main(String[] args) {
RestaurantFactory factory = new RestaurantFactory();
Restaurant kfc = factory.createRestaurant(RestaurantFactory.RestaurantType.KFC);
Restaurant pizzaHut = factory.createRestaurant(RestaurantFactory.RestaurantType.PizzaHut);
kfc.getCook();
pizzaHut.getWaiter();
}
}
class RestaurantFactory {
enum RestaurantType {
KFC, PizzaHut
}
Restaurant createRestaurant(RestaurantType type) {
switch (type) {
case KFC: return new KFC();
case PizzaHut: return new PizzaHut();
default: System.out.format("Invalid restaurant %s", type); return null;
}
}
}
输出:
1
2
I'm KFC cook.
I'm PizzaHut waiter.
UML:
比如Spring
中的BeanFactory使用的就是这种模式:getBean
方法用Bean
的类型/名字作为参数,返回对应的Bean
。
JDK
中的Calendar
类,也是用的这种模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static Calendar getInstance(TimeZone zone, Locale aLocale) {
...
// 根据参数创建不同的Calendar实例
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
...
}
如果实例类型相对固定,那么上述模式能很好的满足需求。但是如果实例类型不确定,那么每当需要新增类型的时候,都需要改动原先的方法,对开闭原则遵循得不好。于是有了第二种类型。
3.2 工厂类型2
依然是饭店的例子,我们通过如下方式定义工厂:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
public static void main(String[] args) {
KFCFactory kfcFactory = new KFCFactory();
PizzaHutFactory pizzaHutFactory = new PizzaHutFactory();
Restaurant kfc = kfcFactory.createRestaurant();
Restaurant pizzaHut = pizzaHutFactory.createRestaurant();
kfc.getCook();
pizzaHut.getWaiter();
}
}
class KFCFactory {
Restaurant createRestaurant() {
return new KFC();
}
}
class PizzaHutFactory {
Restaurant createRestaurant() {
return new PizzaHut();
}
}
输出:
1
2
I'm KFC cook.
I'm PizzaHut waiter.
UML:
上述方式,每新增一个类别,只需要新增一个对应的工厂即可,原先的工厂方法以及实例无需做任何修改。如LoggerFactory就是这种类型的工厂模式:
1
2
3
4
5
6
public static Logger getLogger(String name) {
// getLogger最终是委托给ILoggerFactory去做的。
// 新增Logger获取方式,只需新增ILoggerFactory的实现类,扩展性很强。
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
但是这种方式,对每一种类别,都要有一个工厂类,复杂度相对较高。实际中,还是第一种类型使用比较频繁。
4 总结
工厂方法模式是使用很广泛的一种创建型模式,几乎能在所有的开源框架中见到。很典型的特点就是,工厂类以Factory
字样结尾:-)。