简单工厂模式

简单工厂模式

1. 介绍和应用场景

工厂模式:创建对象时不会对客户端暴露创建逻辑(new),而是通过使用一个共同的接口来指向新创建的对象。

生活中的例子:您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现

代码例子:如果在程序中需要创建很多个同一系列的类,比如:加法类减法类乘法类除法类都完成了计算(getResult)方法,或者圆类正方形类三角形类都完成了计算面积(getArea)方法,那么就是同一个系列的类。这些类都实现了相同的方法,这时候就可以考虑使用简单工厂模式了。


2. 步骤

  1. 接口/父类

因为都属于一系列的类,所以可以抽象出接口或者一个父类,是不是抽象类都可以,关键是明白这是一系列类的上层

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
/***
* 这个类是所有运算类的父类 拥有运算的基本属性
*/
public class Operation {
private double numberA;
private double numberB;

public double getNumberA() {
return numberA;
}

public void setNumberA(double numberA) {
this.numberA = numberA;
}

public double getNumberB() {
return numberB;
}

public void setNumberB(double numberB) {
this.numberB = numberB;
}

/**
* @return 计算的结果
* @throws Exception 运算时的异常
*/
public double GetResult() throws Exception {
double result = 0;
return result;
}
}

  1. 一堆同一系列的类,都继承/实现了上面的父类/接口
1
2
3
4
5
6
7
8
9
/**
* 加法类
*/
public class OperationAdd extends Operation {
@Override
public double GetResult() {
return getNumberA() + getNumberB();
}
}
1
2
3
4
5
6
7
8
9
/**
* 减法类
*/
public class OperationSub extends Operation {
@Override
public double GetResult() {
return getNumberA()-getNumberB();
}
}
1
2
3
4
5
6
7
8
9
/**
* 乘法类
*/
public class OperationMul extends Operation {
@Override
public double GetResult() {
return getNumberA()*getNumberB();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/***
* 除法类
*/
public class OperationDiv extends Operation {
@Override
public double GetResult() throws Exception {
if (getNumberB() == 0) {
throw new Exception("除数不能为 0 !");
}
return getNumberA() / getNumberB();
}
}

此时结构(idea生成的图是有点复杂,看一条线就够了)


  1. 工厂类,到这里就知道为什么要抽象出接口/父类了,因为我们并不知道用户想创建加法还是减法,但是返回值一定是一个统一的Operation类。就像买车的例子一样,虽然不知道客户买什么车,但来了车厂,买的一定是车而不是飞机。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 简单工厂类 含有一个静态方法
* 用于根据运算符返回具体的运算实例
*/
public class OperationFactory {
public static Operation createOperate(String operate) {
Operation operation = null;
switch (operate) {
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
case "*":
operation = new OperationMul();
break;
case "/":
operation = new OperationDiv();
break;
}
return operation;
}
}

  1. 调用
1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) throws Exception {
Operation operation = OperationFactory.createOperate("+");
operation.setNumberA(1);
operation.setNumberB(2);
// 这里其实是由Operation具体的子类实现的具体功能
double result = operation.GetResult();
System.out.println(result);
}
}

最终的结构为(我p调了多余的线)


3. 优化

我们自己写的switch函数,所以当然知道有哪些情况。但是假如我们这个程序写的很大了,甚至写成了框架供别人使用。

从用户的角度来说:仔细看main函数,我们在写的时候传了”+”这个符号,可是用户怎么知道switch里面有+呢(要么用户查看手册文档,要么用户查看源码),这就很麻烦,如果可以用一个枚举供用户选择就太好了

1
2
3
4
5
6
7
//供用户选择的枚举变量
public enum OpType {
ADD,
SUB,
MUL,
DIV
}

工厂类要修改的地方

效果


另外,就是学习spring的时候提到过BeanFactory有一种getBean方式(点击空降),也是采用了工厂模式,不过spring情况比较特殊,它不知道用户会创建什么类,也就没办法switch,因此采用的是反射的方式,那么写法就是这个样子

1
2
3
4
5
6
7
8
9
10
11
12
13
public class OperationFactory {
public static <T> T createOperate(Class<? extends T> clazz) {
T obj = null;

try {
obj = (T) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}

return obj;
}
}

main方法的修改(当然如果还想采用枚举类优化的话,枚举类中就要存类名了。)

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) throws Exception {
Operation operation = OperationFactory.createOperate(OperationAdd.class); //就改了这里
operation.setNumberA(1);
operation.setNumberB(2);
// 这里其实是由Operation具体的子类实现的具体功能
double result = operation.GetResult();
System.out.println(result);
}
}

使用反射的好处是不需要写很多if,switch,但是反射会降低效率,只是告诉大家spring采用过这种方式,知道一下即可。



参考链接

https://blog.csdn.net/qq_41113081/article/details/88170881

https://www.runoob.com/design-pattern/factory-pattern.html

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×