简介: 介绍简单工厂( Simple Factory )的概念、使用场景以及详细的分析介绍。

1. 概念

简单工厂模式( Simple Factory Pattern )是指由一个工厂对象决定创建出哪一种产品类的实例,但它不属于 GOF 23 种设计模式。

2. 使用场景

适用于用工厂创建对象较少的场景,只需要关注传入工厂方法的参数,并不需要关心内部具体的实现逻辑。

3. 代码实现

我们以在线教育平台为例,在平台会有各种各样的教程视频,那么我们可以在这里抽象出一个 Course 的接口,所有的具体课程实现这个课程接口,场景代码如下:

  • course 接口
1
2
3
4
5
6
7
8
public interface Course {

/**
* 课程录制方法
*/
void liveCourse();

}
  • java 课程的具体实现:
1
2
3
4
5
6
public class JavaCourse implements Course{
@Override
public void liveCourse() {
System.out.println("正在直播Java课程···");
}
}
  • 那么我们在使用时如下代码:
1
2
3
4
public static void main( String[] args ){
Course course = new JavaCourse();
course.liveCourse();
}

我们用 Course 来只想 JavaCourse 的具体实现,这也是 Java 中多态的体现, 也就是在业务层实际依赖的是 JavaCourse ,那么在后期我们如果扩展了 Python 的课程,那么实现 PythonCourse 即可,但是随着业务的增加,后期增加越来越多的课程时我们的依赖会变得非常臃肿,我们期望把创建具体课程类的细节隐藏起来,对用户来说都是 Course 。下面我们通过工厂的形式来进行优化设计。

  • 实现一个 PythonCourse
1
2
3
4
5
6
7
8
9
public class PythonCourse implements Course {
/**
* 课程录制方法
*/
@Override
public void liveCourse() {
System.out.println("python课程正在录制···");
}
}
  • 创建一个类 CourseFactory ,专门用来创建具体的课程对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CourseFactory {

/**
* 创建课程对象
* @param name 需要创建对象名
* @return
*/
public Course create(String name){
if ("java".equals(name)){
return new JavaCourse();
}else if ("python".equals(name)){
return new PythonCourse();
}
return null;
}
}
  • 我们调用也做相应的修改
1
2
3
4
5
6
7
8
9
public static void main( String[] args ){
CourseFactory factory = new CourseFactory();
//创建Java课程
Course java = factory.create("java");
java.liveCourse();
//创建python课程
Course python = factory.create("python");
python.liveCourse();
}

这里我们可以看到,在调用时我们只需要告诉工厂我们需要哪个对象,而并不需要暴露具体的实现,当然这里为了方便我们是可以把 create 方法改为静态方法。

  • 我们可以看一下上述代码的UML图

simple-Factory 类图

这样的过程对于客户端来说,调用变得简单了很多,但是相应的也出现了新的问题,因为这样的设计是不符合开闭原则的,我们以后每增加一个课程类都需要去修改工厂中的创建方法,所以我们可以进行优化,采用反射来实现:

  • 对工厂类进行优化,采用反射机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CourseFactory {
/**
* 创建对象
* @param className 需要被创建对象的全路径
* @return
*/
public static Course create(String className){

try {
if (!Objects.isNull(className) && !"".equals(className)){
Class<?> aClass = Class.forName(className);
Course course = (Course) aClass.newInstance();
return course;
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
  • 客户端调用代码
1
2
3
4
5
public static void main(String[] args) {
Course course = CourseFactory.create("org.design.demo2.JavaCourse");
course.liveCourse();
}

这样修改之后,随着课程增加内部具体实现都是不变的,但是传入的字符串的 className 可控性有待挺高,所以我们这里可以进一步优化

  • CourseFactory 修改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @param clazz 传入需要创建对象的Class
* @return
*/
public static Course create(Class<? extends Course> clazz) {
try {
if (!Objects.isNull(clazz)){
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
  • 客户端调用
1
2
3
4
public static void main(String[] args) {
Course course = CourseFactory.create(JavaCourse.class);
course.liveCourse();
}

上述类图

4. 在源码中体现

1. Calendar

CalendarcreateCalendar(TimeZone zone,Locale aLocale) 就是一个简单工厂的使用,在 getInstance(TimeZone zone, Locale aLocale) 进入

  • 我们看一下 createCalendar 具体实现源码
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
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}

Calendar cal = null;

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;
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}

最后

  • 简单工厂适合于这种对象创建简单并且较少的场景
  • 简单工厂中工厂的职责过于太重,不易于扩展复杂的产品结构

----------

微信公众号


欢迎关注公众号“云栖简码”