Java 设计模式之策略模式

策略模式简介

定义

定义一系列的算法,把每一个算法封装起来,并且使它们可以相互替换。策略模式能使算法与用户端而独立存在与变化。

例子

某商场为迎接国庆的到来,决定开启商品促销活动,活动细节如下
针对不同会员等级优惠的折扣有所不同,同时与消费金额所关联,消费金额在不同范围折扣优惠率也不一样。

  • 普通会员:没有任何优惠折扣
  • 初级会员:消费100-200元有5%的促销折扣,消费200(含200)以上元有10%促销折扣
  • 中级会员:消费100-200元有10%的促销折扣,消费200(含200)以上元有15%促销折扣
  • 高级会员:消费100-200元有15%的促销折扣,消费200(含200)以上元有20%促销折扣

最后需计算促销折扣后,实付金额。

为了实现上面的需求,通常我们会这样写,如下:

public class StragetyTest {

    public static void main(String[] args) {
        StragetyTest test = new StragetyTest();
        test.getPayAmount("初级会员", 150);
        test.getPayAmount("中级会员", 150);
        test.getPayAmount("高级会员", 150);
    }


    public double getPayAmount(String memberType, double amount) {
        double payAmout=0;
        switch (memberType) {
        case "初级会员":
            if (amount>=100 && amount<200 ) {
                //促销折扣5%
                payAmout=amount*0.95;
            }else if (amount>=200) {
                //促销折扣10%
                payAmout=amount*0.9;
            }else {
                //没有优惠折扣
                payAmout=amount;
            }
            break;
        case "中级会员":
            if (amount>=100 && amount<200 ) {
                //促销折扣10%
                payAmout=amount*0.9;
            }else if (amount>=200) {
                //促销折扣15%
                payAmout=amount*0.85;
            }else {
                //没有优惠折扣
                payAmout=amount;
            }
            break;
        case "高级会员":
            if (amount>=100 && amount<200 ) {
                //促销折扣15%
                payAmout=amount*0.85;
            }else if (amount>=200) {
                //促销折扣20%
                payAmout=amount*0.8;
            }else {
                //没有优惠折扣
                payAmout=amount;
            }
            break;

        default:
            //没有优惠折扣
            payAmout=amount;
            break;
        }
        System.out.println("原价:"+amount+"元,"+memberType+"优惠后的实际价格:"+payAmout+"元");
        return payAmout;

    }

}

上面的代码,需求倒是实现了,如果活动促销规则临时有变,需要对普通会员消费有促销力度……此时就在增加对普通会员优惠金额的计算了,在原代码需增加if-else判断语句了,如果每个会员等级的商品促销折扣算法变的更复杂,各种判断语言交错其中,那么代码就会变的特别浮肿,后期维护扩展困难,增加开发的成本。这可违背了设计模式中的单一职责原则。
下面通过策略模式,进行重构。如下:
1、定义一个用于计算促销折扣后的实际价格的接口(策略接口)

public interface MemberStrategy {

    double getPayAmount(double amount);

}

2、分别定义三个会员等级的具体产品类,它们共同的特点实现了计算促销优惠后的价格(具体策略实现)

//初级会员
public class PrimaryMemberStrategy implements MemberStrategy{

    @Override
    public double getPayAmount(double amount) {
        double payAmout;
        if (amount>=100 && amount<200 ) {
            //促销折扣5%
            payAmout=amount*0.95;
        }else if (amount>=200) {
            //促销折扣10%
            payAmout=amount*0.9;
        }else {
            //没有优惠折扣
            payAmout=amount;
        }
        System.out.println("原价:"+amount+"元,初级会员优惠后的实际价格:"+payAmout+"元");
        return payAmout;
    }

}
//中级会员
public class MiddleMemberStrategy implements MemberStrategy{

    @Override
    public double getPayAmount(double amount) {
        double payAmout;
        if (amount>=100 && amount<200 ) {
            //促销折扣10%
            payAmout=amount*0.9;
        }else if (amount>=200) {
            //促销折扣15%
            payAmout=amount*0.85;
        }else {
            //没有优惠折扣
            payAmout=amount;
        }
        System.out.println("原价:"+amount+"元,中级会员优惠后的实际价格:"+payAmout+"元");
        return payAmout;
    }

}
//高级会员

public class AdvancedMemberStrategy implements MemberStrategy{

    @Override
    public double getPayAmount(double amount) {
        double payAmout;
        if (amount>=100 && amount<200 ) {
            //促销折扣15%
            payAmout=amount*0.85;
        }else if (amount>=200) {
            //促销折扣20%
            payAmout=amount*0.8;
        }else {
            //没有优惠折扣
            payAmout=amount;
        }
        System.out.println("原价:"+amount+"元,高级会员优惠后的实际价格:"+payAmout+"元");
        return payAmout;
    }

}

3、定义一个控制器,包含了每个会员等级,可以传递不同具体会员对象来调用需要的方法。

public class MemberContext {

    private MemberStrategy mStrategy;

    //使用多态来接受对象值。
    public void setMemberStrategy(MemberStrategy mStrategy) {
        this.mStrategy = mStrategy;
    }

    public double getPayAmount(double amount){
        return mStrategy.getPayAmount(amount);
    }

}

4、用户使用

public class StragetyTest {
    public static void main(String[] args) {
         MemberContext memberContext = new MemberContext();
         //初级会员
         memberContext.setMemberStrategy(new PrimaryMemberStrategy());
         memberContext.getPayAmount(150);
         //中级会员
         memberContext.setMemberStrategy(new MiddleMemberStrategy());
         memberContext.getPayAmount(150);
         //高级会员
         memberContext.setMemberStrategy(new AdvancedMemberStrategy());
         memberContext.getPayAmount(150);

    }
}

最后的结果:

原价:150.0元,初级会员优惠后的实际价格:142.5元
原价:150.0元,中级会员优惠后的实际价格:135.0元
原价:150.0元,高级会员优惠后的实际价格:127.5元

可以看出,使用策略模式,把每个会员等级促销算法独立出来,互不影响,也避免了对会员等级switch-case判断语句,降低了代码的耦合性,有利于维护,即使增加对普通会员的促销策略,也无需改动原来的代码,只需新增策略具体类即可,不仅体现了设计模式中职责单一原则,还体现了开放封闭原则,
同时缺点也会明显,如果策略过多,会增加过多的策略类,最后导致类的数量剧增。

针对上面的策略模式的例子,用UML类图体现如下:

UML结构图

这里写图片描述

角色介绍:

  • MemberStrategy:抽象策略角色,算法方法的抽象,通常是接口
  • Strategy:具体策略类,实现了抽象策略接口,算法的具体实现。
  • MemberContext:上下文角色,用来操控每个策略类的上下文环境。屏蔽了高层模块对具体算法的访问,提现了设计模式中的依赖倒置原则。

使用场景

在开发中,凡是有同类型类似的算法需求,可以尝试使用策略模式进行封装来应对不同的变化。