Java设计模式(三)简单工厂模式

定义与类型

  • 定义:由一个工厂对象决定创建出哪一种产品类的实例
  • 类型:创建型,但不属于GOF23种设计模式

适用场景

  • 工厂类负责创建的对象比较少
  • 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心

优点

只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节

缺点

工厂类的职责相对过重,增加新的产品,需要修改工厂类的判断逻辑,违背开闭原则

Coding

创建一个抽象产品类

1
2
3
public abstract class Video {
public abstract void produce();
}

产品实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python视频");
}
}

创建产品对应的简单工厂,通过产品类型来创建产品,应用方无需知道创建产品的细节

1
2
3
4
5
6
7
8
9
10
11
12
public class VideoFactory {

public Video getVideo(String type) {
if ("java".equalsIgnoreCase(type)) {
return new JavaVideo();
} else if ("python".equalsIgnoreCase(type)) {
return new PythonVideo();
} else {
return null;
}
}
}

测试类

1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo("Java");
video.produce();
}
}

控制台输出

1
录制Java课程

如果增加产品,我们不仅需要修改产品对应的产品类,还需要修改工厂类,违反了开闭原则。

我们可以通过反射来优化下我们的工厂类

1
2
3
4
5
6
7
8
9
10
11
public class VideoFactory {

public Video getVideo(Class<? extends Video> clazz) {
try {
return clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}

这样一来,添加产品的时候不用再修改我们的工厂类,而是直接添加产品即可。

最终的UML类图

源码解析

JDK源码

在JDK中,使用简单工厂模式的例子如java.util.Calendar,一组getInstance的重载方法,提供了创建Calendar产品的简单工厂方法。

1
2
3
4
public static Calendar getInstance()
public static Calendar getInstance(TimeZone zone)
public static Calendar getInstance(Locale aLocale)
public static Calendar getInstance(TimeZone zone,Locale aLocale)

核心方法为

1
private static Calendar createCalendar(TimeZone zone,Locale aLocale)

源码较长,不贴了,有兴趣的可以去看下源码。

Calendar的UML类图如下

Logback源码

logback类中的简单工厂模式主要体现在ch.qos.logback.classic.LoggerContext#getLogger(String)

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
@Override
public final Logger getLogger(final String name) {

if (name == null) {
throw new IllegalArgumentException("name argument cannot be null");
}

// 判断log类型返回root节点的logger
if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
return root;
}

int i = 0;
Logger logger = root;

// 如果缓存中已经存在的指定的logger,直接返回childLogger
Logger childLogger = (Logger) loggerCache.get(name);
// if we have the child, then let us return it without wasting time
if (childLogger != null) {
return childLogger;
}

// 以下是创建logger的逻辑
String childName;
while (true) {
int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
if (h == -1) {
childName = name;
} else {
childName = name.substring(0, h);
}
// move i left of the last point
i = h + 1;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -1) {
return childLogger;
}
}
}

是一个典型的简单工厂方法

0%