先说一下总结:
工厂+策略并没有完全解决问题,而改进的策略模式才真正的带来便利。
如果把判断方式抽象成一个方法,在每一个策略的实现类中,就可以不用工厂模式了。
而且,每次修改都不用改判断逻辑部分,每添加一种策略,就在策略内实现判断,判断通过的返回策略对象。
问题:
随着业务代码的复杂度增加,我们的代码中出现了大量的 if-else,一坨一坨的,类也特别大,非常难看,而且还伴随着大量的重复代码,特别容易隐藏坑,比如增加或修改场景时,需要把所有重复的代码都改一遍,如果有遗漏就会产生bug。
目的:
所以要拆解类,要对重复代码抽象。
如何实现:
使用工厂模式、策略模式、工厂+策略模式对代码拆解。
工厂模式:
随场景不断优化,
- 简单场景使用 if-else 即可解决问题(V0版本);
- 如果代码复杂可以使用封装把类拆开(V1版本);
- 如果场景可能发生变化,频繁改动运行良好的代码不好,代码使用继承,使增加或修改尽可能的少影响线上运行代码,即工厂模式(V2版本)。
package com.designMode.factory.V0;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 如何实现一个计算器控制台程序,这是最简单直接的方法
*
* 分析:
* 1、变量命名不规范 A B C
* 2、对除法/的分子没有判断0
*
* 0、最严重的是思路问题,完全没有发挥面向对象的特征,封装,继承,多态
*
* 该怎么做:
* 1、编程的原则,用尽可能的办法避免重复
* 2、把功能分开,此处是控制台相关的,和计算相关的,请看V1版本
*
* @author zhangna12
* @date 2019-07-06
*/
public class Calculator {
public static void main(String[] args) {
try{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入数字A:");
String A = br.readLine();
System.out.println("请输入数字B:");
String B = br.readLine();
System.out.println("请输入运算符号(+ - * /):");
String C = br.readLine();
String D = "";
if("+".equals(C)){
D = String.valueOf(Double.parseDouble(A) + Double.parseDouble(B));
}else if("-".equals(C)){
D = String.valueOf(Double.parseDouble(A) - Double.parseDouble(B));
}else if("*".equals(C)){
D = String.valueOf(Double.parseDouble(A) * Double.parseDouble(B));
}else if("/".equals(C)){
if(Double.parseDouble(B) == 0){
System.out.println("除数不能为0");
}else{
D = String.valueOf(Double.parseDouble(A) / Double.parseDouble(B));
}
}
System.out.println("A=" + A + ",B=" + B + ",符号:" + C + ",结果:" + D );
}catch (Exception e){
System.out.println("未知异常");
}
}
}
package com.designMode.factory.V1;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 面向对象的思想,封装
*
* 分析:
* 1、每要增加一种计算规则,就会使原来运行良好的代码产生变化,风险太大
*
* 改进:
* 1、把每种计算规则分开,增加、修改不会影响其他,请看V2
*
* @author zhangna12
* @date 2019-07-06
*/
public class Calculator {
public static void main(String[] args) {
try{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入数字A:");
String A = br.readLine();
System.out.println("请输入数字B:");
String B = br.readLine();
System.out.println("请输入运算符号(+ - * /):");
String C = br.readLine();
String D = "";
D = String.valueOf(Operation.getResult(Double.parseDouble(A),Double.parseDouble(B),C));
System.out.println("A=" + A + ",B=" + B + ",符号:" + C + ",结果:" + D );
}catch (Exception e){
System.out.println("未知异常");
}
}
}
package com.designMode.factory.V1;
/**
* 封装-计算相关的类
*
* @author zhangna12
* @date 2019-07-06
*/
public class Operation {
public static double getResult(double numA, double numB, String operate){
double result = 0d;
switch (operate){
case "+":
result = numA + numB;
break;
case "-":
result = numA + numB;
break;
case "*":
result = numA * numB;
break;
case "/":
if(numB == 0){
System.out.println("除数不能为0");
}else {
result = numA / numB;
}
break;
}
return result;
}
}
package com.designMode.factory.V2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 简单工厂模式
* 面向对象的思想,封装、继承
*
* @author zhangna12
* @date 2019-07-06
*/
public class Calculator {
public static void main(String[] args) {
try{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入数字A:");
String A = br.readLine();
System.out.println("请输入数字B:");
String B = br.readLine();
System.out.println("请输入运算符号(+ - * /):");
String C = br.readLine();
String D = "";
Operation operation = OperationFactory.createOperation(C);
operation.setNumA(Double.parseDouble(A));
operation.setNumB(Double.parseDouble(B));
D = String.valueOf(operation.getResult());
System.out.println("A=" + A + ",B=" + B + ",符号:" + C + ",结果:" + D );
}catch (Exception e){
System.out.println("未知异常");
}
}
}
package com.designMode.factory.V2;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public class OperationFactory {
public static Operation createOperation(String operate){
Operation operation = null;
switch (operate){
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
case "*":
operation = null;
break;
case "/":
operation = null;
break;
}
return operation;
}
}
package com.designMode.factory.V2;
/**
* 继承-分离计算规则
*
* @author zhangna12
* @date 2019-07-06
*/
public abstract class Operation {
private Double numA;
private Double numB;
public abstract double getResult();
public Double getNumA() {
return numA;
}
public void setNumA(Double numA) {
this.numA = numA;
}
public Double getNumB() {
return numB;
}
public void setNumB(Double numB) {
this.numB = numB;
}
}
package com.designMode.factory.V2;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public class OperationAdd extends Operation {
@Override
public double getResult() {
return getNumA() + getNumB();
}
}
package com.designMode.factory.V2;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public class OperationSub extends Operation {
@Override
public double getResult() {
return getNumA() - getNumB();
}
}
策略模式:
随场景不断优化,
- 基本场景(V0版本)
- 增加场景,使用 switch 判断(V1版本)
- 当增加的场景比较多,使用工厂模式(V2版本)
- 如果场景变化的又多又频繁,需要频繁的改动工厂类不好,使用策略模式(V3版本)
- 如何才能在频繁的改动下,最小的改动线上代码,且易维护,使用工厂+策略模式(V4版本,其实并没有解决问题)
- V5版本,参考 https://mp.weixin.qq.com/s/XQDZ9M0qLBz0xo-7O6t2oA 中策略模式的实现(懒得写了,自己看吧)。
package com.designMode.strategy.V0;
/**
* 场景:
* 商场收银软件,输入物品单价和数量,点击ok输出商品清单和总计
*
* 分析:
* 1、如果场景变化,该怎么实现,请看V1
*
* @author zhangna12
* @date 2019-07-06
*/
public class Shopping {
static double total = 0d;
private static void okClick(double price, double num){
double totalPrice = price*num;
total = total + totalPrice;
System.out.println("单价:" + price + ",数量:" + num + ",合计:" + totalPrice + ",总计:" + total);
}
public static void main(String[] args) {
okClick(12,2);
okClick(1,3);
}
}
package com.designMode.strategy.V1;
/**
* 场景:
* 商场收银软件,输入物品单价和数量,点击ok输出商品清单和总计
* 增加场景:打8折,打7折,打5折
*
* 分析:
* 1、每个打折的分支除了折扣力度不一样之外,其他都一样,可以考虑重构
* 2、如果再增加返利的场景呢,满300-100,满500-200
*
* 改进:
* 1、如果使用简单工厂,写一个父类,在继承实现多个打折和返利的子类,如果遇到场景叠加也要增加子类,比如打折的基础上再返利
* 面向对象的编程,并不是类越多越好,类的划分是封装,但分类的基础是抽象,具有相同属性和功能的对象抽象集合才是类
*
* 2、按计算方式考虑,打折的算一类,返利算一类,请看V2
*
* @author zhangna12
* @date 2019-07-06
*/
public class Shopping {
static double total = 0d;
private static void okClick(double price, double num, StrategyEnum se){
double totalPrice = 0d;
switch (se){
case DIS_EIGHT:
totalPrice = price*num*0.8;
break;
case DIS_FIVE:
totalPrice = price*num*0.5;
break;
}
total = total + totalPrice;
System.out.println("单价:" + price + ",数量:" + num + ",合计:" + totalPrice + ",总计:" + total);
}
public static void main(String[] args) {
okClick(12,2, StrategyEnum.DIS_FIVE);
okClick(1,3, StrategyEnum.DIS_EIGHT);
}
}
package com.designMode.strategy.V1;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public enum StrategyEnum {
DIS_FIVE,
DIS_EIGHT;
}
package com.designMode.strategy.V2;
/**
* 场景:
* 简单工厂实现
* 商场收银软件,输入物品单价和数量,点击ok输出商品清单和总计
* 增加场景:打8折,打7折,打5折,满300-100,满500-200
*
* 分析:
* 1、比如商场的活动又频繁又多,每次增加或改动活动类型,都要去改动工厂方法,所以可以找更好的办法
*
* 改进:
* 1、使用策略模式,请看V3
*
* @author zhangna12
* @date 2019-07-06
*/
public class Shopping {
static double total = 0d;
private static void okClick(double price, double num, StrategyEnum se){
double totalPrice = 0d;
Cash cash = CashFactory.createCash(se);
cash.getCash(price*num);
total = total + totalPrice;
System.out.println("单价:" + price + ",数量:" + num + ",合计:" + totalPrice + ",总计:" + total);
}
public static void main(String[] args) {
okClick(12,2, StrategyEnum.RETURN_300_100);
okClick(1,3, StrategyEnum.REBATE_EIGHT);
}
}
package com.designMode.strategy.V2;
/**
* 1、没有构造函数,直接返回策略对象
*
* @author zhangna12
* @date 2019-07-06
*/
public class CashFactory {
public static Cash createCash(StrategyEnum se){
Cash cash = null;
switch (se){
case NORMAL:
cash = new RebateCash(1);
break;
case REBATE_FIVE:
cash = new RebateCash(0.5);
break;
case REBATE_EIGHT:
cash = new RebateCash(0.8);
break;
case RETURN_300_100:
cash = new ReturnCash(300,100);
break;
case RETURN_500_200:
cash = new ReturnCash(500,200);
break;
}
return cash;
}
}
package com.designMode.strategy.V2;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public abstract class Cash {
public abstract double getCash(double money);
}
package com.designMode.strategy.V2;
/**
* 打折收费
*
* @author zhangna12
* @date 2019-07-06
*/
public class RebateCash extends Cash {
/**
* 折扣率
*/
private double moneyRebate = 1d;
public RebateCash(double moneyRebate) {
this.moneyRebate = moneyRebate;
}
@Override
public double getCash(double money) {
return money * moneyRebate;
}
}
package com.designMode.strategy.V2;
/**
* 满减
*
* @author zhangna12
* @date 2019-07-06
*/
public class ReturnCash extends Cash {
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
public ReturnCash(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double getCash(double money) {
double result = money;
if(money > moneyCondition){
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
package com.designMode.strategy.V2;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public enum StrategyEnum {
NORMAL,
REBATE_FIVE,
REBATE_EIGHT,
RETURN_300_100,
RETURN_500_200;
}
package com.designMode.strategy.V3;
/**
* 场景:
* 策略模式实现
* 商场收银软件,输入物品单价和数量,点击ok输出商品清单和总计
* 增加场景:打8折,打7折,打5折,满300-100,满500-200
*
* 分析:
* 1、比如商场的活动又频繁又多,每次增加或改动活动类型,这个不用改CashContext了
* 2、但是判断的过程不属于页面的操作
*
* 改进:
* 1、使用工厂+策略模式,请看V4
*
* @author zhangna12
* @date 2019-07-06
*/
public class Shopping {
static double total = 0d;
private static void okClick(double price, double num, StrategyEnum se){
CashContext cashContext = null;
switch (se){
case NORMAL:
cashContext = new CashContext(new RebateCash(1));
break;
case REBATE_FIVE:
cashContext = new CashContext(new RebateCash(0.5));
break;
case REBATE_EIGHT:
cashContext = new CashContext(new RebateCash(0.8));
break;
case RETURN_300_100:
cashContext = new CashContext(new ReturnCash(300,100));
break;
case RETURN_500_200:
cashContext = new CashContext(new ReturnCash(300,200));
break;
}
double totalPrice = 0d;
totalPrice = cashContext.getResult(price*num);
total = total + totalPrice;
System.out.println("单价:" + price + ",数量:" + num + ",合计:" + totalPrice + ",总计:" + total);
}
public static void main(String[] args) {
okClick(12,2, StrategyEnum.RETURN_300_100);
okClick(1,3, StrategyEnum.REBATE_EIGHT);
}
}
package com.designMode.strategy.V3;
/**
* 1、构造函数传入策略对象,方法返回计算结果
*
* @author zhangna12
* @date 2019-07-06
*/
public class CashContext {
private Cash cash;
public CashContext(Cash cash) {
this.cash = cash;
}
public double getResult(double money){
return cash.getCash(money);
}
}
package com.designMode.strategy.V3;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public abstract class Cash {
public abstract double getCash(double money);
}
package com.designMode.strategy.V3;
/**
* 打折收费
*
* @author zhangna12
* @date 2019-07-06
*/
public class RebateCash extends Cash {
/**
* 折扣率
*/
private double moneyRebate = 1d;
public RebateCash(double moneyRebate) {
this.moneyRebate = moneyRebate;
}
@Override
public double getCash(double money) {
return money * moneyRebate;
}
}
package com.designMode.strategy.V3;
/**
* 满减
*
* @author zhangna12
* @date 2019-07-06
*/
public class ReturnCash extends Cash {
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
public ReturnCash(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double getCash(double money) {
double result = money;
if(money > moneyCondition){
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
package com.designMode.strategy.V3;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public enum StrategyEnum {
NORMAL,
REBATE_FIVE,
REBATE_EIGHT,
RETURN_300_100,
RETURN_500_200;
}
package com.designMode.strategy.V4;
/**
* 场景:
* 工厂+策略模式实现
* 商场收银软件,输入物品单价和数量,点击ok输出商品清单和总计
* 增加场景:打8折,打7折,打5折,满300-100,满500-200
*
* 分析:
* 1、比如商场的活动又频繁又多,每次增加或改动活动类型,这个不用改CashContext了
* 2、但是判断的过程放到CashContext中
*
* 思考:
* 这个工厂模式有啥区别,还不都是场景增加,修改判断的逻辑,只不过一个返回的是类,一个返回的是计算结果
*
* 改进:
* 如果这个判断方式抽象成一个方法,在每一个策略的实现类中,就可以不用工厂模式了
* 而且,每次修改都不用改判断逻辑部分,每添加一种策略,就在策略内实现判断,判断通过的返回
* 详情见:https://mp.weixin.qq.com/s/XQDZ9M0qLBz0xo-7O6t2oA
*
* 综上:
* 工厂+策略并没有完全解决问题,而是改进的策略模式才真正的带来便利
*
* @author zhangna12
* @date 2019-07-06
*/
public class Shopping {
static double total = 0d;
private static void okClick(double price, double num, StrategyEnum se){
CashContext cashContext = new CashContext(se);
double totalPrice = 0d;
totalPrice = cashContext.getResult(price*num);
total = total + totalPrice;
System.out.println("单价:" + price + ",数量:" + num + ",合计:" + totalPrice + ",总计:" + total);
}
public static void main(String[] args) {
okClick(12,2, StrategyEnum.RETURN_300_100);
okClick(1,3, StrategyEnum.REBATE_EIGHT);
}
}
package com.designMode.strategy.V4;
/**
* 1、使用构造函数获得策略对象
* 2、构造函数中传入的是策略,不是策略对象
* 3、返回计算结果
*
* @author zhangna12
* @date 2019-07-06
*/
public class CashContext {
private Cash cash;
/**
* 简单工厂的应用
* @param se
*/
public CashContext(StrategyEnum se) {
switch (se){
case NORMAL:
cash = new RebateCash(1);
break;
case REBATE_FIVE:
cash = new RebateCash(0.5);
break;
case REBATE_EIGHT:
cash = new RebateCash(0.8);
break;
case RETURN_300_100:
cash = new ReturnCash(300,100);
break;
case RETURN_500_200:
cash = new ReturnCash(300,200);
break;
}
}
public double getResult(double money){
return cash.getCash(money);
}
}
package com.designMode.strategy.V4;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public abstract class Cash {
public abstract double getCash(double money);
}
package com.designMode.strategy.V4;
/**
* 打折收费
*
* @author zhangna12
* @date 2019-07-06
*/
public class RebateCash extends Cash {
/**
* 折扣率
*/
private double moneyRebate = 1d;
public RebateCash(double moneyRebate) {
this.moneyRebate = moneyRebate;
}
@Override
public double getCash(double money) {
return money * moneyRebate;
}
}
package com.designMode.strategy.V4;
/**
* 满减
*
* @author zhangna12
* @date 2019-07-06
*/
public class ReturnCash extends Cash {
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
public ReturnCash(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double getCash(double money) {
double result = money;
if(money > moneyCondition){
result = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return result;
}
}
package com.designMode.strategy.V4;
/**
* 请填写类的描述
*
* @author zhangna12
* @date 2019-07-06
*/
public enum StrategyEnum {
NORMAL,
REBATE_FIVE,
REBATE_EIGHT,
RETURN_300_100,
RETURN_500_200;
}