02-工厂模式

参考资料1:http://c.biancheng.net/design_pattern/

参考资料2:https://refactoringguru.cn/design-patterns/catalog

  • 开发中有一个非常重要的原则“开闭原则”,对拓展开放、对修改关闭;
  • 工厂模式主要负责对象创建的问题;
  • 通过反射进行工厂模式的设计,完成动态的对象创建
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
public class TestNewInstanceForFile {
public static void main(String[] args) throws Exception {
// 创建出入流对象, files\\application.txt 内容:com.methods.Teacher
FileReader fr = new FileReader("files\\application.txt");
BufferedReader br = new BufferedReader(fr);
// 读出文件中的类的全限定名
String className = br.readLine();
// 创建Object对象返回后进行强转为对应类型,进而使用
Teacher t3 = (Teacher)createObject(className);
System.out.println(t3);

br.close();
}

/**
* 工厂:创建对象工厂
* @param className String类型的类的全限定名
* @return Object Object类型的对象
*/
public static Object createObject(String className) {
try {
Class<?> c = Class.forName(className);
return c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

1. 简单工厂模式

简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建出哪一种产品类 的实例。

优点:

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。
  3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点:

  1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
  2. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  3. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
  4. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

应用场景:

对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

1
2
3
public interface ICourse {
void record(); //录制视频
}
1
2
3
4
5
public class JavaCourse implements ICourse {
public void record() {
System.out.println("录制 Java 课程");
}
}
1
2
3
4
5
6
7
//客户端调用代码
public class SimpleFactoryTest {
public static void main(String[] args) {
ICourse course = new JavaCourse();
course.record();
}
}

1.1 隐藏细节

如果业务扩展,我继续增加 PythonCourse 甚至更多,那么我们客户端的 依赖会变得越来越臃肿。因此,我们要想办法把这种依赖减弱,把创建细节隐藏

1
2
3
4
5
public class PythonCourse implements ICourse {
public void record() {
System.out.println("录制 Python 课程");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
//创建 CourseFactory 工厂类 - 隐藏创建细节
public class CourseFactory {
public ICourse create(String name) {
if ("java".equals(name)) {
return new JavaCourse();
} else if ("python".equals(name)) {
return new PythonCourse();
} else {
return null;
}
}
}
1
2
3
4
5
6
7
//修改客户端调用代码
public class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory factory = new CourseFactory();
factory.create("java");
}
}

image-20220312220427287

1.2 使用反射

如果我们业务继续扩展,要增加前端课程,那么工厂中的 create() 就要根据产品链的丰富每次都要修改代码逻辑。不符合开闭原则。因此,我们对简单工厂还可以继续优化,可以采用反射技术

1
2
3
4
5
6
7
8
9
10
11
12
13
//创建 CourseFactory 工厂类 - 使用反射技术
public class CourseFactory {
public ICourse create(Class<? extends ICourse> clazz) {
try {
if (null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
6
7
8
//修改客户端调用代码
public class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory factory = new CourseFactory();
ICourse course = factory.create(JavaCourse.class);
course.record();
}
}

image-20220312220345535

简单工厂模式在 JDK 源码也是无处不在,如 Calendar 类,看 Calendar.getInstance()方法:

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

再比如 logback,可以看到 LoggerFactory 中有多个重载的方法 getLogger():

1
public static Logger getLogger(Class clazz) { return getLogger(clazz.getName()); }

2. 工厂方法模式

工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

优点:

  • 创建对象需要大量重复的代码。
  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
  • 一个类通过其子类来指定创建哪个对象。

缺点:

  • 类的个数容易过多,增加复杂度
  • 增加了系统的抽象性和理解难度
  • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

应用场景:

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌

根据单一职责原则我们将职能继续拆分,专人干专事。Java 课程由 Java 工厂创建, Python 课程由 Python 工厂创建,对工厂本身也做一个抽象。

1
2
3
public interface ICourseFactory {
ICourse create();
}
1
2
3
4
5
public class JavaCourseFactory implements ICourseFactory {
public ICourse create() {
return new JavaCourse();
}
}
1
2
3
4
5
public class PythonCourseFactory implements ICourseFactory {
public ICourse create() {
return new PythonCourse();
}
}
1
2
3
4
5
6
7
8
9
10
11
//修改客户端调用代码
public class FactoryMethodTest {
public static void main(String[] args) {
ICourseFactory factory = new PythonCourseFactory();
ICourse course = factory.create();
course.record();
factory = new JavaCourseFactory();
course = factory.create();
course.record();
}
}

image-20220312220315121

3. 抽象工厂模式

抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。

使用抽象工厂模式一般要满足以下条件:

  • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

优点:
抽象工厂模式除了具有工厂方法模式的优点外,其他主要如下:

  • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
  • 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

缺点:

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

image-20220312220721892

image-20220312220922483

1
2
3
public interface IVideo {
void record();
}
1
2
3
public interface INote {
void edit();
}
1
2
3
4
5
6
//创建一个抽象工厂 CourseFactory 类
//抽象工厂是用户的主入口在 Spring 中应用得最为广泛的一种设计模式,易于扩展
public interface CourseFactory {
INote createNote();
IVideo createVideo();
}
1
2
3
4
5
6
//创建 Java 产品族:Java 视频 JavaVideo 类
public class JavaVideo implements IVideo {
public void record() {
System.out.println("录制 Java 视频");
}
}
1
2
3
4
5
6
//创建 Java 产品族:Java 课堂笔记 JavaNote 类 - 扩展产品等级
public class JavaNote implements INote {
public void edit() {
System.out.println("编写 Java 笔记");
}
}
1
2
3
4
5
6
7
8
9
//创建 Java 产品族的具体工厂 JavaCourseFactory
public class JavaCourseFactory implements CourseFactory {
public INote createNote() {
return new JavaNote();
}
public IVideo createVideo() {
return new JavaVideo();
}
}
1
2
3
4
5
6
//创建 Python 产品族:Python 视频 PythonVideo 类
public class PythonVideo implements IVideo {
public void record() {
System.out.println("录制 Python 视频");
}
}
1
2
3
4
5
6
//创建 Python 产品族:Python 课堂笔记 PythonNote 类 - 扩展产品等级
public class PythonNote implements INote {
public void edit() {
System.out.println("编写 Python 笔记");
}
}
1
2
3
4
5
6
7
8
9
//创建 Python 产品族的具体工厂 PythonCourseFactory
public class PythonCourseFactory implements CourseFactory {
public INote createNote() {
return new PythonNote();
}
public IVideo createVideo() {
return new PythonVideo();
}
}
1
2
3
4
5
6
7
8
//客户端调用代码 - 抽象工厂
public class AbastractFactoryTest {
public static void main(String[] args) {
JavaCourseFactory factory = new JavaCourseFactory();
factory.createNote().edit();
factory.createVideo().record();
}
}

上面的代码完整地描述了两个产品族 Java 课程Python 课程,也描述了两个产品等级 视频手记


02-工厂模式
https://janycode.github.io/2018/04/28/10_设计模式/02-工厂模式/
作者
Jerry(姜源)
发布于
2018年4月28日
许可协议