设计模式讲解与代码实践(二十二)——策略

本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!

1 目的

策略(Strategy)模式用于对算法进行封装,并可对算法进行替换。

2 基本形态

策略的基本形态如类图2-1所示。
图2-1  策略类图
图2-1 策略类图

3 参与者

结合图2-1,下面介绍各类在策略设计模式中扮演的角色。
3.1 Strategy
Strategy是策略接口,定义了各算法接口方法。
3.2 ConcreteStrategy
ConcreteStrategy是具体策略,实现了接口Strategy。
3.3 Context
Context是上下文。Context在内部维护了策略Strategy的实例,必要时调用Strategy实例提供的算法方法。

4 代码实践

下面我们用一个业务场景实例来进一步讲解策略的使用。
4.1 场景介绍
某移动通讯公司提供A、B两种话费套餐,其账单系统可以通过用户电话号码输出用户话费账单明细。
以下各节将介绍该场景各类的具体实现及其在策略设计模式中所对应的参与者角色。
4.2 ICommFeeCalc
ICommFeeCalc是通讯费计算器接口,定义通讯费计算的相关接口方法。对应于策略模式的参与者,ICommFeeCalc是策略接口Strategy。下面的代码给出了ICommFeeCalc的声明。

package demo.designpattern.strategy;
import java.util.Date;


/**
 * 通讯费计算器接口
 * Created by LiMingzi on 2017/11/30.
 */
public interface ICommFeeCalc {
    /**
     * 计算通讯费
     * @param fromDate 起始时间
     * @param toDate 终止时间
     * @return 通讯费
     */
    double calculate(Date fromDate,Date toDate);
}

上述代码中,16行,声明计算通讯费方法calculate,根据通话的起止时间计算通讯费。
4.3 CommFeeCalcForPkgA
CommFeeCalcForPkgA是套餐A通讯费计算器类,实现了通讯费计算器接口ICommFeeCalc。对应于策略模式的参与者,CommFeeCalcForPkgA是具体策略ConcreteStrategy。下面的代码给出了CommFeeCalcForPkgA的声明。

package demo.designpattern.strategy;

import java.util.Date;

import static java.lang.Math.ceil;

/**
 * 套餐A通讯费计算器
 * Created by LiMingzi on 2017/11/30.
 */
public class CommFeeCalcForPkgA implements ICommFeeCalc {
    /**
     * 计算通讯费
     *
     * @param fromDate 起始时间
     * @param toDate   终止时间
     * @return 通讯费
     */
    @Override
    public double calculate(Date fromDate, Date toDate) {
        // 通话时长(分钟)
        double talkTime = Math.ceil(((toDate.getTime()-fromDate.getTime())/1000.0/60.0));
        // 通话费用
        double cost;
        if(talkTime>3){
            cost = 0.5+(talkTime-3)*0.1;
        }else{
            cost = 0.5;
        }
        return cost;
    }
}

上述代码中,25-29行,对于套餐A,用户前3分钟通话费用为0.5元,之后每分钟通讯费0.1元。不满3分钟按3分钟计费,不满整分钟向上凑整(如5.1分钟按6分钟计算)。
4.4 CommFeeCalcForPkgB
CommFeeCalcForPkgB是套餐B通讯费计算器,实现了通讯费计算器接口ICommFeeCalc。对应于策略模式的参与者,CommFeeCalcForPkgB是具体策略ConcreteStrategy。下面的代码给出了CommFeeCalcForPkgB的声明。

package demo.designpattern.strategy;

import java.util.Date;

/**
 * 套餐B通讯费计算器
 * Created by LiMingzi on 2017/11/30.
 */
public class CommFeeCalcForPkgB implements ICommFeeCalc {
    /**
     * 计算通讯费
     *
     * @param fromDate 起始时间
     * @param toDate   终止时间
     * @return 通讯费
     */
    @Override
    public double calculate(Date fromDate, Date toDate) {
        // 通话时长(分钟)
        double talkTime = Math.ceil(((toDate.getTime()-fromDate.getTime())/1000.0/60.0));
        return talkTime*0.15;
    }
}

上述代码中,21行,对于套餐B,通讯费按照每分钟0.15元收取,不满整分钟向上凑整(如5.1分钟按6分钟计算)。
4.5 CommBill
CommBill是通讯账单类,实现了用户账单的打印功能。对应于策略模式的参与者,CommBill是上下文Context。下面的代码给出了CommBill的声明。

package demo.designpattern.strategy;

import java.util.Date;
import java.util.*;

/**
 * 通讯账单类
 * Created by LiMingzi on 2017/12/5.
 */
public class CommBill {
    /**
     * 用户电话号码
     */
    private String userTel;
    /**
     * 费用计算器
     */
    private ICommFeeCalc commFeeCalc;

    /**
     * 构造方法
     * @param userTel 用户电话号码
     * @param commFeeCalc 通讯费用个计算器实例
     */
    public CommBill(String userTel, ICommFeeCalc commFeeCalc) {
        this.userTel = userTel;
        this.commFeeCalc = commFeeCalc;
    }

    /**
     * 获取通讯记录(demo)
     * @return 通讯记录集合,其中每条通讯记录名值对含义如下:
     * id:String,记录id;
     * toTel:String,拨叫号码;
     * fromDate:Date,通话开始时间
     * toDate:Date,通话结束时间
     */
    private List<Map<String,Object>>getRecords(){
        // 记录集合
        List<Map<String,Object>> records=new ArrayList<Map<String, Object>>();
        if("15011559999".equals(userTel)){
            // 记录1
            Map<String,Object>record1 = new HashMap<String,Object>();
            record1.put("id","001");
            record1.put("toTel","114");
            record1.put("fromDate",new Date(2017,12,5,8,50,20));
            record1.put("toDate",new Date(2017,12,5,8,51,1));
            records.add(record1);
            // 记录1
            Map<String,Object>record2 = new HashMap<String,Object>();
            record2.put("id","002");
            record2.put("toTel","13436533333");
            record2.put("fromDate",new Date(2017,12,5,8,52,50));
            record2.put("toDate",new Date(2017,12,5,8,58,12));
            records.add(record2);
        }else{
            // 记录A
            Map<String,Object>recordA = new HashMap<String,Object>();
            recordA.put("id","001");
            recordA.put("toTel","15835456987");
            recordA.put("fromDate",new Date(2017,12,3,18,10,32));
            recordA.put("toDate",new Date(2017,12,3,18,11,42));
            records.add(recordA);
        }
        return records;
    }

    /**
     * 打印账单
     */
    public void print(){
        // 通话记录集合
        List<Map<String,Object>>records = getRecords();
        System.out.println("用户"+userTel+"共有"+records.size()+"条通话记录:");
        for (Map<String, Object> record : records) {
            System.out.println("记录id:"+record.get("id"));
            System.out.println("拨叫号码:"+record.get("toTel"));
            // 开始时间
            Date fromDate = (Date)record.get("fromDate");
            // 结束时间
            Date toDate = (Date)record.get("toDate");
            System.out.println("通话开始时间:"+fromDate);
            System.out.println("通话结束时间:"+toDate);
            System.out.println("通讯费用:"+commFeeCalc.calculate(fromDate,toDate)+"元");
            System.out.println("--------------------------------------------------------------");
        }
    }
}

上述代码中,18行,ICommFeeCalc类型的成员变量commFeeCalc维护了费用计算器实例,该实例在构造方法中初始化(27行);38行,获取通讯录记录方法getRecords是演示方法,返回指定用户的通讯记录;71行,打印账单方法print,遍历用户通讯记录打印账单;84行,调用commFeeCalc的calculate方法计算通讯费用。
4.6 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。测试代码中,电话号码为15011559999的用户选择了套餐A,电话号码为13436533333的用户选择了套餐B,通过打印的账单明细不难发现在计算通讯费时我们使用了不同的算法。

/**
     * 策略测试
     */
    public static void strategyTest(){
        // 通讯账单实例1
        CommBill commBill1 = new CommBill("15011559999",new CommFeeCalcForPkgA());
        commBill1.print();
        // 通讯账单实例2
        CommBill commBill2 = new CommBill("13436533333",new CommFeeCalcForPkgB());
        commBill2.print();
    }

编译运行后,得到如下测试结果:
用户15011559999共有2条通话记录:
记录id:001
拨叫号码:114
通话开始时间:Sat Jan 05 08:50:20 CST 3918
通话结束时间:Sat Jan 05 08:51:01 CST 3918
通讯费用:0.5元


记录id:002
拨叫号码:13436533333
通话开始时间:Sat Jan 05 08:52:50 CST 3918
通话结束时间:Sat Jan 05 08:58:12 CST 3918
通讯费用:0.8元


用户13436533333共有1条通话记录:
记录id:001
拨叫号码:15835456987
通话开始时间:Thu Jan 03 18:10:32 CST 3918
通话结束时间:Thu Jan 03 18:11:42 CST 3918
通讯费用:0.3元


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值