简单工厂设计模式
案例: 写一个计算器, 实现加减乘除.
分析: 1. 有两个数, num1 和 num2.
2. 有一个操作符
3. 有一个业务逻辑运算
第一步: 拿到这个业务, 我们直观的思考, 可以想象到这个业务非常简单:
package com.designModel.chapter1_simpleFactory.step1; import java.io.IOException; import java.util.Scanner; /** * 计算器: 实现两个数的加, 减, 乘, 除 * * 第一步思路. 输入两个数, 一个运算符. 执行运算 * */ public class Caculator { public static void main(String[] args) { try { String num1; String num2; String operate; int result = 0; System.out.println("输入num1:"); Scanner scan = new Scanner(System.in); num1 = scan.nextLine(); System.out.println("输入num2:"); scan = new Scanner(System.in); num2 = scan.nextLine(); System.out.println("输入运算符:"); scan = new Scanner(System.in); operate = scan.nextLine(); int n1 = Integer.parseInt(num1); int n2 = Integer.parseInt(num2); switch(operate){ case "+": result = n1 + n2; break; case "-": result = n1 - n2; break; case "*": result = n1 * n2; break; case "/": result = n1 / n2; break; } System.out.println("运算结果:" + result); } catch (Exception e) { e.printStackTrace(); } } /** * 这样操作的问题: 前端输入和后端逻辑运算完全混合在一起. 维护起来及其不方便 * 那么如果优化, 第一步就是讲前端输入和业务逻辑分开处理 */ }
分析:
这样操作的问题:
前端输入和后端逻辑运算完全混合在一起. 维护起来非常不方便
这里完全没有使用到面向对象的特征. 下面回顾面向对象的特征
1. 可维护性: 程序出现问题了, 方便维护修改, 且不会影响到其他正常的业务逻辑 2. 可复用性: 一段代码可以在多处被使用 3. 可扩展性: 有新的需求, 在不改变原来逻辑的基础上方便的增减 4. 灵活性: 无论是放在哪里, 稍微改变就可实现新的业务逻辑.
从这四个方面考虑. 上面这段代码, 维护性差, 改一个地方很可能或误改正确的地方; 复用性, 除了计算器, 其他地方基本不可用. 扩展性也不好, 添加一个开方, 可能误改加减乘除. 灵活性, 就没有.
那么. 如何使用面向对象的方式来修改呢?
第二步. 将业务逻辑和前端页面相分离
package com.designModel.chapter1_simpleFactory.step2; import java.io.IOException; import java.util.Scanner; /** * 计算器: 实现两个数的加, 减, 乘, 除 * * 第一步思路. 输入两个数, 一个运算符. 执行运算 * -------------------------------------- * 再上一个案例的基础上, 将前端和后端逻辑分开处理 * */ public class Caculator { public int caculate(int n1, int n2, String operate){ int result = 0; switch(operate){ case "+": result = n1 + n2; break; case "-": result = n1 - n2; break; case "*": result = n1 * n2; break; case "/": result = n1 / n2; break; } return result; } public static void main(String[] args) {
//前端输入 try { String num1; String num2; String operate; int result = 0; System.out.println("输入num1:"); Scanner scan = new Scanner(System.in); num1 = scan.nextLine(); System.out.println("输入num2:"); scan = new Scanner(System.in); num2 = scan.nextLine(); System.out.println("输入运算符:"); scan = new Scanner(System.in); operate = scan.nextLine(); int n1 = Integer.parseInt(num1); int n2 = Integer.parseInt(num2); Caculator c = new Caculator(); result = c.caculate(n1, n2, operate); System.out.println("运算结果:" + result); } catch (Exception e) { e.printStackTrace(); } } /** * 现在计算逻辑和前端数据处理的逻辑分开了. 可是依然有问题, 问题在哪里呢? 计算业务逻辑将耦合性太强 * 比如: 我现在要添加一个算法,开平方根. 这时候, 需要在也逻辑处进行修改, 添加一个switch分支. 这是后, 如果业务逻辑大的话, * 很可能会误操作更改了之前已经写好的的业务逻辑. * * 例如: 我们的薪资系统 * 原来只有技术人员(月薪), 销售人员(底薪+提成), 经理(年薪+股份)三种运算方法, 现在要增加兼职工作人员(时薪)的算法. * * 按照我们上面的代码逻辑, 公司必须把包含原三种算法的运算累给你, 让你修改. 这是, 如果你有点私心, 小算盘一打, 除了增加时薪外, * 还修改了技术人员的薪资 * salary = salary * 1.1; * 那就意味着你的月薪会增加10%. 本来是让你加一个功能的, 结果却修改了另一个功能. 这个风险太大了. * * 所以: 我们要改进这种算法. 分离加减乘除, 让其中一个运算修改, 不会影响到另外几个. * 这就用到了面向对象的三大特性: 封装, 继承 和 多态. * */ }
正如上面代码的总结:
/** * 现在计算逻辑和前端数据处理的逻辑分开了. 可是依然有问题, 问题在哪里呢? 计算业务逻辑将耦合性太强
* * 比如: 我现在要添加一个算法,开平方根. 这时候, 需要在业务逻辑处进行修改, 添加一个switch分支. 这时, 如果业务逻辑大的话, * 很可能会误操作更改了之前已经写好的的业务逻辑. * * 例如: 我们的薪资系统 * 原来只有技术人员(月薪), 销售人员(底薪+提成), 经理(年薪+股份)三种运算方法, 现在要增加兼职工作人员(时薪)的算法. * * 按照我们上面的代码逻辑, 公司必须把包含原三种算法的运算类给你, 让你修改. 这时, 如果你有点私心, 小算盘一打, 除了增加时薪外, * 还修改了技术人员的薪资 * salary = salary * 1.1; * 那就意味着你的月薪会增加10%. 本来是让你加一个功能的, 结果却修改了另一个功能. 这个风险太大了. * * 所以: 我们要改进这种算法. 分离加减乘除, 让其中一个运算修改, 不会影响到另外几个.
* * 这就用到了面向对象的三大特性: 封装, 继承 和 多态. * */
第三步:将输入参数封装成对象, 将加减乘除的业务逻辑单独出来一个类,
package com.designModel.chapter1_simpleFactory.step3; public class Operation { private double num1; private double num2; public double getResult(){ double result = 0; return result; } public double getNum1() { return num1; } public void setNum1(double num1) { this.num1 = num1; } public double getNum2() { return num2; } public void setNum2(double num2) { this.num2 = num2; } }
package com.designModel.chapter1_simpleFactory.step3.biz; import com.designModel.chapter1_simpleFactory.step3.Operation; public class OperationAdd extends Operation{ @Override public double getResult() { return super.getNum1() + super.getNum2(); } }
package com.designModel.chapter1_simpleFactory.step3.biz; import com.designModel.chapter1_simpleFactory.step3.Operation; public class OperationSub extends Operation{ @Override public double getResult() { return super.getNum1() - super.getNum2(); } }
package com.designModel.chapter1_simpleFactory.step3.biz; import com.designModel.chapter1_simpleFactory.step3.Operation; public class OperationMul extends Operation{ @Override public double getResult() { return super.getNum1() * super.getNum2(); } }
package com.designModel.chapter1_simpleFactory.step3.biz; import com.designModel.chapter1_simpleFactory.step3.Operation; public class OperationDiv extends Operation{ @Override public double getResult() { if(super.getNum2() == 0){ try { throw new Exception("除数不能为零"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return super.getNum1() / super.getNum2(); } }
这时候, 我在增加一个开平方根的算法, 就不会影响到其他四个类的算法了? 那么, 如何让计算器知道我要用那个算法呢?
现在的问题就是如何实例化对象了. ---- 简单工厂设计模式.
package com.designModel.chapter1_simpleFactory.step3; import com.designModel.chapter1_simpleFactory.step3.biz.OperationAdd; import com.designModel.chapter1_simpleFactory.step3.biz.OperationDiv; import com.designModel.chapter1_simpleFactory.step3.biz.OperationMul; import com.designModel.chapter1_simpleFactory.step3.biz.OperationSub; public class OperationFactory { public static Operation createOperate(String operate) { Operation ope = null; switch(operate){ case "+": ope = new OperationAdd(); break; case "-": ope = new OperationSub(); break; case "*": ope = new OperationMul(); break; case "/": ope = new OperationDiv(); break; } return ope; } }
在使用的时候, 你只需像下面这样操作就可以了.
public static void main(String[] args) { Operation ope = new Operation(); ope = OperationFactory.createOperate("+"); ope.setNum1(10); ope.setNum2(20); System.out.println(ope.getResult()); }
使用了工厂设计模式以后, 当你想要增加一个开平方根的算法时, 要怎么做呢?
1. 增加一个开平方根的类继承自Operation.
2. 在工厂中增加一个case分支.
搞定!
一个小小的计算器, 也可以使用算法. 那么我们在使用其他的方式的时候, 更要考虑算法了?
---------------------------------------------------------------------
总结:
在实际项目中,简单工厂模式是我们使用的最多的设计模式之一,简单工厂模式在应对需求变更的过程中也起到了很大的作用。
使用情景:
再不确定会有多少个处理操作时应该考虑使用简单工厂模式,如针对同样的接收到的数据,处理的逻辑可能会不同,可能以后还会增加新的操作。
案例:
例如如果实现计算器的功能时,对于同样的输入数据,可能执行加、减、乘、除,甚至其他的功能。因此可以抽象出一个操作的抽象类或是接口,提供一个统一的处理方法(此处为process),然后每种操作创建出一个子类出来。而判断具体使用哪个具体的实现类是在工厂类中进行判断的(将存放操作的变量传递给工厂的生产方法)。工厂类始终返回的是这个抽象类,这样如果对原有功能进行更改或是新添加新的功能,也不会对原来的其他类做修改,只编译修改的那个类或是新的类就可以了。
这样就做到了把耦合降到最低,同时也便于维护。
注意:如果客户提出要再添加一个功能来处理这些数据,实现起来就灰常方便了
-----------------------------------------------------------------------
UML类图
这个计算器类的UML图如下:
下面来学习一下UML类图的画法
总结:
空心三角形+实现-->继承
空心三角形+虚线-->实现接口
实线+箭头-->关联关系
徐建+箭头-->依赖关系
实心菱形+实线+箭头-->组合关系
空心菱形+实线+箭头-->聚合关系
-----------------------------------------------------------------------
简单工厂设计模式, 在项目中, 可以使用的场景
1. 支付: 支付有支付宝支付, 微信支付, 还有银联支付. 以后还可能有新的支付方式. 这时候就可以使用简单工厂设计模式.
处理方式:
斜体表示抽象类: 例如Pay类