场景优化-工厂模式和策略模式(非常实用且常用的设计模式)

先说一下总结:
工厂+策略并没有完全解决问题,而改进的策略模式才真正的带来便利。
如果把判断方式抽象成一个方法,在每一个策略的实现类中,就可以不用工厂模式了。
而且,每次修改都不用改判断逻辑部分,每添加一种策略,就在策略内实现判断,判断通过的返回策略对象。

问题:
随着业务代码的复杂度增加,我们的代码中出现了大量的 if-else,一坨一坨的,类也特别大,非常难看,而且还伴随着大量的重复代码,特别容易隐藏坑,比如增加或修改场景时,需要把所有重复的代码都改一遍,如果有遗漏就会产生bug。

目的:
所以要拆解类,要对重复代码抽象。

如何实现:
使用工厂模式、策略模式、工厂+策略模式对代码拆解。

工厂模式:
随场景不断优化,

  1. 简单场景使用 if-else 即可解决问题(V0版本);
  2. 如果代码复杂可以使用封装把类拆开(V1版本);
  3. 如果场景可能发生变化,频繁改动运行良好的代码不好,代码使用继承,使增加或修改尽可能的少影响线上运行代码,即工厂模式(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();
    }
}

策略模式:
随场景不断优化,

  1. 基本场景(V0版本)
  2. 增加场景,使用 switch 判断(V1版本)
  3. 当增加的场景比较多,使用工厂模式(V2版本)
  4. 如果场景变化的又多又频繁,需要频繁的改动工厂类不好,使用策略模式(V3版本)
  5. 如何才能在频繁的改动下,最小的改动线上代码,且易维护,使用工厂+策略模式(V4版本,其实并没有解决问题)
  6. 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;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值