大话设计模式--第二章 策略设计模式

策略设计模式

现在有一个需求: 给商场做一个收银软件. 营业员根据客户购买的产品的单价和数量, 向客户打印小票。

这个实现很简单. 一个类就可以搞定:

package com.designModel.chaper2_strategeModel.step1;

import java.util.Scanner;

/**
 * cash收银
 * @author samsung
 *
 */
public class Cash {
    public String list = "";
    public Double totalPrice = 0.00;
    
    public void buttonOK(){
        
        Scanner scan = new Scanner(System.in);
        System.out.println("输入单价:");
        String price = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入数量:");
        String num = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入折扣:");
        String zhekou = scan.nextLine();
        
        Double xiaoji = Double.parseDouble(price) * Integer.parseInt(num) * Double.parseDouble(zhekou)/10;
        list += "单价:" + price +", 数量: "+ num +",折扣: " + zhekou + "\n";
        totalPrice += xiaoji;
        
        
    }
    
    public static void main(String[] args) {
        Cash cash = new Cash();
        boolean flag = true;
        while(flag) {
            cash.buttonOK();
            if(cash.totalPrice > 10){
                flag = false;
            }
        }
        
        System.out.println("========================");
        System.out.println("清单:\n " + cash.list);
        System.out.println("总价: "+ cash.totalPrice);
    }
    
}

但是: 用面向对象的角度思考. 这个类将前端输入和业务逻辑混合在一块了. 不利于维护, 扩展, 复用, 也不灵活. 

假如: 现在商场搞活动, 所有商品打折, 7折, 

过一段时间, 商场又搞活动, 所有商品打5折

国庆节, 商品满200减50.

 

如果按照上面的方式来写代码, 那么每次都要写一遍, 如何将其复用起来呢? 并且每次增加新的活动的时候, 又不会影响到原来的活动. 

对了, 简单工厂设计模式, 我们之前刚刚学过的. 下面看看简单工厂设计模式的UML图:

首先, 有一个工厂类, 在这个工厂类里面, 根据类型, 依赖于不同的现金收费方式. 具体代码如下:

首先与一个抽象的现金收费方式类:

package com.designModel.chaper2_strategeModel.step2;

/**
 * 现金收费类
 * @author samsung
 *
 */
public abstract class CashFee {
    
    public abstract double acceptCash(double money); 
}

定义现金收费方式的实现类,分别是: 正常收费类, 折扣类, 返现类

package com.designModel.chaper2_strategeModel.step2;

//正常收费类
public class NormalCashFee extends CashFee {

    @Override
    public double acceptCash(double money) {
        return money;
    }
}
package com.designModel.chaper2_strategeModel.step2;

public class DiscountCashFee extends CashFee {

    private double discount = 0.00;
    
    public DiscountCashFee(double discount){
        this.discount = discount / 10;
    }
    
    @Override
    public double acceptCash(double money) {
        
        return this.discount * money;
    }

    public double getDiscount() {
        return discount;
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }

}
package com.designModel.chaper2_strategeModel.step2;

public class ReturnCashFee extends CashFee {

    //基础金额
    private double baseCash;
    
    //返现金额
    private double returnCash;
    
    public ReturnCashFee(double baseCash, double returnCash){
        this.baseCash = baseCash;
        this.returnCash = returnCash;
    }
    
    @Override
    public double acceptCash(double money) {
        return money - Math.floor(money/300) * 50;
    }
    

    public double getBaseCash() {
        return baseCash;
    }

    public void setBaseCash(double baseCash) {
        this.baseCash = baseCash;
    }

    public double getReturnCash() {
        return returnCash;
    }

    public void setReturnCash(double returnCash) {
        this.returnCash = returnCash;
    }
    
}

在定义一个工厂类, 用来产生各种现金收费方式

package com.designModel.chaper2_strategeModel.step2;

import java.util.Scanner;


public class CashFeeFactory {
    public static CashFee createCashFee(int type, double discount, double basePrice, double returnPrice){
        CashFee cashFee = null;
        switch(type){
            case 1:
                cashFee = new NormalCashFee();
                break;
            case 2:
                cashFee = new DiscountCashFee(discount);
                break;
            case 3:
                cashFee = new ReturnCashFee(basePrice, returnPrice);
                break;
        }
        
        return cashFee;
    }
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("输入单价:");
        String price = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入数量:");
        String num = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入折扣类型(1 无折扣 2打折 3满减):");
        String zhekou = scan.nextLine();
        
        double discount = 0.0d;
        double basePrice = 0;
        double returnPrice = 0;
        if("2".equals(zhekou)){
            scan = new Scanner(System.in);
            System.out.println("输入折扣:");
            discount = Double.parseDouble(scan.nextLine());
        }
        
        if("3".equals(zhekou)){
            scan = new Scanner(System.in);
            System.out.println("基础金额:");
            basePrice = Double.parseDouble(scan.nextLine());
            scan = new Scanner(System.in);
            System.out.println("返现金额:");
            returnPrice = Double.parseDouble(scan.nextLine());
        }
        Double xiaoji = Double.parseDouble(price) * Integer.parseInt(num) * Double.parseDouble(zhekou)/10;
        CashFee cashFee = CashFeeFactory.createCashFee(1, discount, basePrice, returnPrice);
    }
}

完成了, 在main方法里, 我们模拟了客户端, 输入单价和数量,打印小票

 -------------------------------------------------------------------------------------------------

思考: 

使用简单工厂设计模式的优缺点:

优点:

  1. 业务逻辑和前端展示相分离开了。业务逻辑的修改, 不影响前端代码

  2. 每一个业务逻辑单独一个类, 修改或者添加一个类, 不会影响到其他的类.

  3. 使用工厂类封装了业务逻辑类. 前端不需要知道到底每种业务逻辑具体怎么实现的. 只需要知道他的父类即可.

缺点:

  1. 如果活动很频繁, 经常会搞各种各样的活动, 那么业务逻辑类就会有很多, 每一次都要增加一个类.

  2. 每增加一个类都要修改工厂类. 修改会非常频繁

 

小结: 简单工厂设计模式虽然也能解决这个问题, 但这个模式只是解决对方的创建问题.

--------------------------------------------------------------------------

下面介绍策略设计模式:

  策略(Strategy)设计模式:定义了算法家族, 分别将算法封装起来, 让他们之间可以相互替换,此模式让算法的变化不会影响到使用算法的客户。

  封装变化点, 是面向对象的一种很重要的思维方式.

  下面以商场促销打印收银小票为例, 来分析策略设计模式

  

  1. 在策略设计模式中, 有各种算法: 所有的算法有一个算法父类: CashFee. 这个父类是一个抽象类. 里面有一个抽象方法acceptCash(double money)

  2. 所有的算法类实现抽象算法父类, 并实现抽象方法

  3. 重点: 有一个上下文对象. 这个对象封装了对算法类的调用

 下面看代码:

1. 现金类算法, 以及现金的各种实现类算法

package com.designModel.chaper2_strategeModel.step3;

/**
 * 现金收费类
 * @author samsung
 *
 */
public abstract class CashFee {
    
    public abstract double acceptCash(double money); 
}
package com.designModel.chaper2_strategeModel.step3;

//正常收费类
public class NormalCashFee extends CashFee {

    @Override
    public double acceptCash(double money) {
        return money;
    }
}
package com.designModel.chaper2_strategeModel.step3;

public class DiscountCashFee extends CashFee {

    private double discount = 0.00;
    
    public DiscountCashFee(double discount){
        this.discount = discount / 10;
    }
    
    @Override
    public double acceptCash(double money) {
        
        return this.discount * money;
    }

    public double getDiscount() {
        return discount;
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }

}
package com.designModel.chaper2_strategeModel.step3;

public class ReturnCashFee extends CashFee {

    //基础金额
    private double baseCash;
    
    //返现金额
    private double returnCash;
    
    public ReturnCashFee(double baseCash, double returnCash){
        this.baseCash = baseCash;
        this.returnCash = returnCash;
    }
    
    @Override
    public double acceptCash(double money) {
        return money - Math.floor(money/300) * 50;
    }
    

    public double getBaseCash() {
        return baseCash;
    }

    public void setBaseCash(double baseCash) {
        this.baseCash = baseCash;
    }

    public double getReturnCash() {
        return returnCash;
    }

    public void setReturnCash(double returnCash) {
        this.returnCash = returnCash;
    }
    
}

最后. 来看看上下文类

package com.designModel.chaper2_strategeModel.step3;

import java.util.Scanner;


public class CashContext {
    private CashFee cashFee;
    public CashContext(CashFee cashFee)
    {
        this.cashFee = cashFee;
    }
    public static double getResult(double money){
        CashFee cashFee = null;
        return cashFee.acceptCash(money);
    }
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("输入单价:");
        String price = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入数量:");
        String num = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入折扣类型(1 无折扣 2打折 3满减):");
        String type = scan.nextLine();
        
        double discount = 0.0d;
        double basePrice = 0;
        double returnPrice = 0;
        
        if("2".equals(type)){
            scan = new Scanner(System.in);
            System.out.println("输入折扣:");
            discount = Double.parseDouble(scan.nextLine());
        }
        
        if("3".equals(type)){
            scan = new Scanner(System.in);
            System.out.println("基础金额:");
            basePrice = Double.parseDouble(scan.nextLine());
            scan = new Scanner(System.in);
            System.out.println("返现金额:");
            returnPrice = Double.parseDouble(scan.nextLine());
        }
        
        
        Double xianjin = Double.parseDouble(price) * Integer.parseInt(num);
        CashContext cc = null;
        switch(type){
            case "1":
                cc = new CashContext(new NormalCashFee());
                break;
            case "2":
                cc = new CashContext(new DiscountCashFee(discount));
                break;
            case "3":
                cc = new CashContext(new ReturnCashFee(basePrice, returnPrice));
                break;
        }
        
        cc.getResult(xianjin);
    }
}

分析:

1. 业务逻辑和前端页面展示分开

2. 有一个context上下文类, 在其内部引用了CashFee类. 构造方法定义了具体的实现类.

3. 这样操作的问题: 客户端依然需要switch判断. 

----------------------------------------------

策略设计模式和简单工厂设计模式相结合的方案:

其他都不变: 变化的是CashContext类

package com.designModel.chaper2_strategeModel.step4;

import java.util.Scanner;


public class CashContext {
    private CashFee cashFee;
    public CashContext(int type, double discount, double basePrice, double returnPrice)
    {
        switch(type){
            case 1:
                cashFee = new NormalCashFee();
                break;
            case 2:
                cashFee = new DiscountCashFee(discount);
                break;
            case 3:
                cashFee = new ReturnCashFee(basePrice, returnPrice);
                break;
      }
    }
    public static double getResult(double money){
        CashFee cashFee = null;
        return cashFee.acceptCash(money);
    }
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("输入单价:");
        String price = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入数量:");
        String num = scan.nextLine();
        
        scan = new Scanner(System.in);
        System.out.println("输入折扣类型(1 无折扣 2打折 3满减):");
        String zhekou = scan.nextLine();
        
        double discount = 0.0d;
        double basePrice = 0;
        double returnPrice = 0;
        
        if("2".equals(zhekou)){
            scan = new Scanner(System.in);
            System.out.println("输入折扣:");
            discount = Double.parseDouble(scan.nextLine());
        }
        
        if("3".equals(zhekou)){
            scan = new Scanner(System.in);
            System.out.println("基础金额:");
            basePrice = Double.parseDouble(scan.nextLine());
            scan = new Scanner(System.in);
            System.out.println("返现金额:");
            returnPrice = Double.parseDouble(scan.nextLine());
        }
        
        
        Double xianjin = Double.parseDouble(price) * Integer.parseInt(num);
        CashContext cc = new CashContext(1, discount, basePrice, returnPrice);
        cc.getResult(xianjin);
    }
}

我们将前端的switch转移到了CashContext的内部. 这样,前端只需要传递给我, 类型信息就可以了. 

下面来看看: 简单工厂设计模式  和 策略模式+简单工厂设计模式的区别:

对于客户端而言: 简单工厂设计模式, 客户端需要知道两个类, 而简单工厂+策略设计模式, 客户端只需要知道一个类, 减低了耦合性.

 ---------------------------------------------------------------------------------------

 思考, 

书上说: 为什么要用策略设计模式呢?

因为商场打折的算法很多, 每次增加一个促销活动, 可能就会有一个新的算法. 这时候, 如果用简单工厂设计模式, 需要增加一个算法类, 同时修改工厂类. 这是, 最好使用策略设计模式.

疑问: 使用策略设计模式. 每次增加一个算法, 也要增加一个算法类, 而且要修改CashContext类. 并没有得到简化呀

应该是我修行不够, 还没能够理解, 忘懂得大神指点.

 

 

 

 

 

 

 

 

猜你喜欢

转载自www.cnblogs.com/ITPower/p/9073386.html