【读书笔记】大话设计模式 第二章 策略模式

2017-08-27  本文已影响0人  孟婆汤不解渴

前言

本篇实现一个模拟收银的小程序, 来了解什么是策略模式, 和策略模式的好处都有哪些, 另外, 不像上一篇博文, 一点一点衍化了, 省略特别简单的版本吧, 直接过渡到有一点封装的版本, 因为能看设计模式的应该都不是刚入行的新手, 有些东西一句话带过就可以了.

正文

场景:

一个简单的收银系统

界面如图


界面

输入价格单价, 根据所选的不同计费方式计算总价的功能, 界面就是为了直观化功能, 其实你在练习时完全可以用控制台模拟输入, 代替此界面, java画这样的界面很不友好.


代码

假设计费方式分为 "正常", "打8折", "满300-100", 分别用0, 1, 2表示, 可以写出如下方法:

// 单价, 数量, 计费方式
public static String operating(double price, double number, int type) {
    double total = 0;
    switch (type) {
    case 0:
        total = price * number;
        break;
    case 1:
        total = price * number * 0.8;
        break;
    case 2:
        total = price - Math.floor((price/300)) * 100;
        break;  
    default:
        break;
    }
    return String.valueOf(total);
}

尽量不要把分支留给客户端处理, 学习了简单工厂模式, 我们可以改造一下, 先像简单工厂模式过度.

首先明确工厂基类, 三者存在共性, 就是都处理同一个价格, 即商品的原价格. 然后分析这几中收费方式, 打折收费, 不管打8折, 打7折, 打几折应该都是打折的算法吧, 所以打折的类算一个实例, 正常的收费算一个实例, 满减活动算一个实例, 这里, 不可能会有人把打1~10折做10个类的对吧?

面向对象的编程, 并不是类越多越好, 类的划分是为了封装, 但分类的基础是抽象, 具有相同属性和功能的对象的抽象集合才是类

父类和子类:

// 处理价格超类
public abstract class CashPaySuper {
    // 处理价格 参数:原价格
    public abstract double calCash(double price);
}
// 正常收费类
public class CashPayNormal extends CashPaySuper {
    @Override
    public double calCash(double price) {
        return price;
    }
}
// 打折收费类
public class CashPayDiscount extends CashPaySuper {
    // 折扣率
    private double bate = 1;
    
    // 初始化时可传入折扣率
    public CashPayDiscount(double bate) {
        this.bate = bate;
    }
    
    @Override
    public double calCash(double price) {
        return price * bate;
    }
}
// 满减收费类
public class CashPayFullSub extends CashPaySuper {
    // 满多少
    double fullMoney = 0;
    // 减多少
    double subMoney = 0;
    
    public CashPayFullSub(double fullMoney, double subMoney) {
        this.fullMoney = fullMoney;
        this.subMoney = subMoney;
    }
    
    @Override
    public double calCash(double price) {
        
        if (price >= fullMoney) {
            price = price - Math.floor((price/fullMoney)) * subMoney;
        }
        return price;
    }
}

简单工厂类:

public class CashPayFactrory {

    public CashPaySuper createCashPay(int strategy) {
        CashPaySuper cs = null;
        switch (strategy) {
        case 0:
            //  正常销售
            cs = new CashPayNormal();
            return cs;
        case 1:
            //  打折销售(传入折扣率)
            cs = new CashPayDiscount(0.8);
            return cs;
        case 2:
            //  满减销售(传入满多少,减多少)
            cs = new CashPayFullSub(300, 100);
            return cs;
        default:
            break;
        }
        return null;
    }
}

客户端的执行代码:

public static String operating(double price, double number, int selectIndex) {
    CashPayFactrory cpf = new CashPayFactrory();
    CashPaySuper cs = cpf.createCashPay(selectIndex);
    return String.valueOf(cs.calCash(price * number));
}

简单工厂模式虽然解决了一定的问题,但这个模式只是解决对象的创建问题, 而且由于工厂本身包括了所有的收费方式, 商场可能要经常性的变更打折额度等,每次维护或扩展收费方式都要改动这个工厂, 以至代码需要重新编译部署, 所以它不是最好的办法, 面对算法的时常变动, 应该有更好的办法.

简单工厂模式

策略模式

不管商场如何促销,其实都是一些算法.用工厂来生成算法对象没有错, 但算法本身只是一种策略, 最重要的是这些算法随时都可能互相替换, 这就是变化点, 而封装变化点是我们面向对象的一种很重要的思维方式.

策略模式的基本结构图

策略模式

我将上述例子重写设计一下:
UML图部分名字我改了, 用来和上面的策略模式学习, 不用在意


收银系统策略模式

CashPaySuper就是抽象策略类, CashPayNormal, CashPayFullSub, CashPayDiscount类就是具体的策略, 所以这四个类不用改动.
只要加一个CashContext类, 再把客户端的执行代码稍微改动一下

CashContext类:

public class CashContext {

    CashPaySuper cashPay;
    
    public CashContext(CashPaySuper cashPay) {
        this.cashPay = cashPay;
    }

    public double Cal(double price) {
        return cashPay.calCash(price);
    }
    
}

执行代码:

public static String operating(double price, double number, int type) {
    CashContext context = null;
    switch (type) {
    case 0:
        context = new CashContext(new CashPayNormal());
        break;
    case 1:
        context = new CashContext(new CashPayDiscount(0.8));
        break;
    case 2:
        context = new CashContext(new CashPayFullSub(300, 100));
        break;  
    default:
        break;
    }
    return String.valueOf(context.Cal(price*number));
}

执行代码和之前的比, 好像又回到了第一版的样子, 又在客户端处理分支了, 所以我们需要把分支想办法挪走.

策略模式与简单工厂模式结合

改造CashContext类:

public class CashContext {

    CashPaySuper cashPay = null;
    
    public CashContext(int type) {
        switch (type) {
        case 0:
            this.cashPay = new CashPayNormal();
            break;
        case 1:
            this.cashPay = new CashPayDiscount(0.8);
            break;
        case 2:
            this.cashPay = new CashPayFullSub(300, 100);
            break;  
        default:
            break;
        }
    }
    
    public double Cal(double price) {
        return cashPay.calCash(price);
    }
    
}

最后改造执行代码:

public static String operating(double price, double number, int type) {
    return String.valueOf(new CashContext(type).Cal(price*number));
}

对比之前的简单工厂的代码:

public static String operating(double price, double number, int selectIndex) {
    CashPayFactrory cpf = new CashPayFactrory();
    CashPaySuper cs = cpf.createCashPay(selectIndex);
    return String.valueOf(cs.calCash(price * number));
}

显然代码更精简了, 暴露给客户端的东西更少了, 仅一个CashContext对象而已, 耦合度降低了.

策略模式解析

策略模式是一种定义一系列算法的方法, 从概念上来看, 所有这些算法的完成都是相同的工作, 只是实现不同, 它可以以相同的方式调用所有的算法, 减少了各种算法类与使用算法之间的耦合.

关于策略模式的总结

参考书籍

上一篇下一篇

猜你喜欢

热点阅读