1. 介绍和应用场景
工厂模式:创建对象时不会对客户端暴露创建逻辑(new),而是通过使用一个共同的接口来指向新创建的对象。
生活中的例子:您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现
代码例子:如果在程序中需要创建很多个同一系列的类,比如:加法类减法类乘法类除法类都完成了计算(getResult)方法,或者圆类正方形类三角形类都完成了计算面积(getArea)方法,那么就是同一个系列的类。这些类都实现了相同的方法,这时候就可以考虑使用简单工厂模式了。
2. 步骤
- 接口/父类
因为都属于一系列的类,所以可以抽象出接口或者一个父类,是不是抽象类都可以,关键是明白这是一系列类的上层
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; }
public double GetResult() throws Exception { double result = 0; return result; } }
|
- 一堆同一系列的类,都继承/实现了上面的父类/接口
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生成的图是有点复杂,看一条线就够了)
- 工厂类,到这里就知道为什么要抽象出接口/父类了,因为我们并不知道用户想创建加法还是减法,但是返回值一定是一个统一的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 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);
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);
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