QQ66o

何为工厂模式

工厂模式是一种创建型模式,工厂只对外暴露创建对象的接口,由外部调用者决定该创建哪个对象。这隐藏了每个功能类具体的实现逻辑,让外部可以更简单的调用。

举个例子:我想要买车,我到4s店和销售说我想要啥啥啥款车,销售就会带着你去看,如果money到位,就安排安排手续,这辆车就是你的了。在这个过程中,4s店就是工厂,而产品就是汽车,你不用去了解汽车是怎么组装的,你只需要报一个型号,4s店就会给你提供你需要的车。

在工厂模式中,每种服务更像是商品一样,摆放在工厂类中,供大家挑选,调用者只需要选取它,工厂就会在内部帮你创建一个该服务的实例,然后你就可以愉快地使用它了。

实战案例

假如我现在要开发一个题库,题库中有选择题、判断题、填空题等,每种题目类型我们需要单独的一套操作接口。

常规做法是在业务代码里加狂加if,判断传入的题目是何种类型的,然后根据类型创建相应的对象,之后就是对题目的CRUD操作。

且不谈业务代码太复杂,耦合性高的问题,就是太多if-else既不美观,维护起来也是相当的难受的。

因此我们可以利用工厂模式改造这处代码。

代码实现

目录结构

image-20240415170432402

Enums

题目种类的枚举,没什么好说的

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
/**
* @Author 林峰
* @Date
* @Version 1.0
*/
public enum SubjectInfoTypeEnums {
RADIO(1,"单选"),
MULTIPLE(2,"多选"),
JUDGE(3,"判断"),
BRIEF(4,"简答")
;

public int code;
public String desc;

SubjectInfoTypeEnums(int code, String desc){
this.code = code;
this.desc = desc;
}

public static SubjectInfoTypeEnums getEnumByCode(int code){
for(SubjectInfoTypeEnums e : SubjectInfoTypeEnums.values()){
if(e.code == code){
return e;
}
}
return null;
}
}

SubjectHandler

SubjectTypeHandler:

1
2
3
4
5
6
7
8
9
10
11
12
public interface SubjectTypeHandler {

/**
* @Description: 获取题目种类枚举
*/
SubjectInfoTypeEnums getHandlerType();
/**
* @Description: 添加题目方法
*/
void add(SubjectInfoBO subjectInfoBO);

}

统一入参出参(每次定义类似于这样的接口时,我才能体会到:接口其实是对类行为的约束这句话,下一句是:继承是对类功能的扩展)

BriefTypeHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 *
* 单选题
*/
public class BriefTypeHandler implements SubjectTypeHandler{
@Override
public SubjectInfoTypeEnums getHandlerType() {
// 返回单选类型
return SubjectInfoTypeEnums.BRIEF;
}

@Override
public void add(SubjectInfoBO subjectInfoBO) {
System.out.println("单选题加入:" + subjectInfoBO.getSubjectName());
}
}

单选题,实现SubjectTypeHandler

JudgeTypeHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 *
* 判断题
*/
public class JudgeTypeHandler implements SubjectTypeHandler{
@Override
public SubjectInfoTypeEnums getHandlerType() {
return SubjectInfoTypeEnums.JUDGE;
}

@Override
public void add(SubjectInfoBO subjectInfoBO) {
System.out.println("判断题加入:" + subjectInfoBO.getSubjectName());
}
}

判断题同样实现SubjectTypeHandler

SubjectFactory

image-20240415171316739

在这个工厂类中,我利用了hashmap存储当前工厂中的产品类

1
2
3
4
//  利用hashmap形式存储当前工厂中的产品类,映射关系为:
// K: 题目种类的枚举code
// V: 特定种类题目的实例化操作对象
private Map<SubjectInfoTypeEnums, SubjectTypeHandler> handlerMap = new HashMap<>();

init()方法,用来初始化工厂中的map,可以理解为将商品放在柜台上

1
2
3
4
5
6
7
8
9
10
11
    public void init(){
if(handlerMap.isEmpty()){
// 为空则初始化
// 初始化:提前将初始化好的handler放入hashmap,这里如果是spring项目,可以
// 利用Spring的自动装配,提前将所有SubjectTypeHandler注入到工厂中,然后放入hashmap
BriefTypeHandler briefTypeHandler = new BriefTypeHandler();
JudgeTypeHandler judgeTypeHandler = new JudgeTypeHandler();
this.handlerMap.put(briefTypeHandler.getHandlerType(),briefTypeHandler);
this.handlerMap.put(judgeTypeHandler.getHandlerType(),judgeTypeHandler);
}
}

getHandler能通过调用者传入的枚举类code,在map中找到对应的handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//    通过hashmap获取对应的handler类
public SubjectTypeHandler getHandler(int subjectValue){
try{
SubjectInfoTypeEnums enumByCode = SubjectInfoTypeEnums.getEnumByCode(subjectValue);
if(enumByCode == null) {
System.out.println("题目类型有误");
return null;
}
return handlerMap.get(enumByCode);
}catch(Exception ex){
System.out.println("未知错误");
return null;
}
}

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FactoryMain {
public static void main(String[] args) {
// 创建工厂类并初始化
SubjectTypeHandlerFactory subjectTypeHandlerFactory = new SubjectTypeHandlerFactory();
subjectTypeHandlerFactory.init();

// 创建模拟题目数据
SubjectInfoBO subjectInfoBO = new SubjectInfoBO();
subjectInfoBO.setId(1L);
subjectInfoBO.setSubjectName("我是判断题!");
subjectInfoBO.setSubjectType(3);
// 调用工厂类,装配对应题目类型的handler
SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfoBO.getSubjectType());
handler.add(subjectInfoBO);

}
}

总结

  • 调用者只需要知道你想要调用的handler的映射,可以是名称,也可以是枚举,然后就能通过工厂获得对象,简化了创建对象的过程
  • 提高了系统扩展性,如果要添加一种新的产品类,比如加入一中心的题型,只需要在枚举中加入相关code,然后实现接口就可以了,在spring下会自动装配到接口的实现列表里,很容易取到然后初始化工厂map