首页 > 代码库 > 策略模式

策略模式

就自己实际产品中用的的模式进行分析:

 策略模式

       本系统中的还款模块涉及到多种还款方式的算法,并且后期可能需要经常性的调整或增减算法,因此本系统采用策略模式来定义这一系列的算法,把它们一个个封装起来,并且使它们可相互替换。使得算法可独立于使用它的客户而变化。

       策略模式的结构示意图:

 

图4-1 策略模式结构图

(1) RepaymentModeI:
       策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。

public interface RepaymentModeI {    /**     * 获得应还利息     *@param annualRate 年利率     *@param loanAount 借款金额     *@param deadline 借款期限     *@param num 还款月数     *@return Double 利息     * */    public Double interest(double annualRate, double loanAount, int deadline, int num);    /**     * 获得应还本金     *@param annualRate 年利率     *@param loanAount 借款金额     *@param deadline 借款期限     *@param num 还款月数     *@return Double 本金     * */    public Double principal(double annualRate, double loanAount, int deadline, int num);    /**     * 获得应还本息     *@param annualRate 年利率     *@param loanAount 借款金额     *@param deadline 借款期限     *@return Double 本息     * */    public Double interAndPri(double annualRate, double loanAount, int deadline, int num);    /**     * 计算利率     * @param annualRate 年利率     * @return 利率     * */    public double rate(double annualRate);}

 

(2) RepaymentModeBy____:
       具体的策略实现,也就是具体的算法实现。目前系统对应有:按天到期还款,按月到期还款(是指每月还息,到期还本的借款方式),按月分期还款(分期还款采用的是通用的"等额本息还款法"),按季分期付款(采用每月付息,按季等额还本的计算方式),实现代码以按月分期还款的计算本金方法为例:

/**     * 按月分期还款:分期还款采用的是通用的"等额本息还款法",即借款人每月以相等的金额偿还贷款本息。也是银行房贷等采用的方法。     * 这里要注意区分 等额本息还款法和等额本金还款法     * @author sl     * */    public Double interAndPri(double annualRate, double loanAount, int deadline, int num) {       /**        * 等额本息还款公式推导 设贷款总额为A,银行月利率为β,总期数为m(个月),月还款额设为X,        * 则各个月所欠银行贷款为:        * 第一个月A(1+β)-X        * 第二个月[A(1+β)-X](1+β)-X = A(1+β)^2-X[1+(1+β)]        * 第三个月{[A(1+β)-X](1+β)-X}(1+β)-X = A(1+β)^3-X[1+(1+β)+(1+β)^2]        *  …        * 由此可得第n个月后所欠银行贷款为:        * A(1+β)^n-X[1+(1+β)+(1+β)^2+…+(1+β)^(n-1)] = A(1+β)^n-X[(1+β)^n-1]/β        * 由于还款总期数为m,也即第m月刚好还完银行所有贷款,因此有:        * A(1+β)^m-X[(1+β)^m-1]/β = 0        * 由此求得:         * X = Aβ(1+β)^m/[(1+β)^m-1]        * */       Return loanAount*rate(annualRate)*Math.pow((1+rate(annualRate)), deadline)/(Math.pow((1+rate(annualRate)), deadline)-1);}

 


(3) Context:

    上下文,负责和具体的策略类交互,通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。

public class Context {    private RepaymentModeI repaymentModeI;    private Double annualRate;                 //年利率    private Double loanAount;                  //借款金额    private int deadline;                      //截止期限(按月计算)    private int num;                           //月数    /**     * 构造方法,传入一个具体的策略对象     * @param repaymentMode 具体的策略对象     */    public Context(RepaymentModeI repaymentMode, Double annualRate,        Double loanAount,int deadline, int num) {       this.repaymentModeI = repaymentMode;       this.annualRate = annualRate;       this.loanAount = loanAount;       this.deadline = deadline;       this.num = num;    }    /**     * 上下文对客户端提供的操作接口,可以有参数和返回值     */    public Double getInterest() {                        //获得利息       //通常会转调具体的策略对象进行算法运算    return repaymentModeI.interest(annualRate, loanAount, deadline, num);    }}

 

策略模式的本质:分离算法,选择实现。
  仔细思考策略模式的结构和实现的功能,会发现,如果没有上下文,策略模式就回到了最基本的接口和实现了,只要是面向接口编程的,那么就能够享受到接口的封装隔离带来的好处。也就是通过一个统一的策略接口来封装和隔离具体的策略算法,面向接口编程的话,自然不需要关心具体的策略实现,也可以通过使用不同的实现类来实例化接口,从而实现切换具体的策略。
  看起来好像没有上下文什么事情,但是如果没有上下文,那么就需要客户端来直接与具体的策略交互,尤其是当需要提供一些公共功能,或者是相关状态存储的时候,会大大增加客户端使用的难度。因此,引入上下文还是很必要的,有了上下文,这些工作就由上下文来完成了,客户端只需要与上下文交互就可以了,这样会让整个设计模式更独立、更有整体性,也让客户端更简单。
  但纵观整个策略模式实现的功能和设计,它的本质还是“分离算法,选择实现”,因为分离并封装了算法,才能够很容易的修改和添加算法;也能很容易的动态切换使用不同的算法,也就是动态选择一个算法来实现需要的功能了。