设计模式,策略模式

871 阅读4分钟

策略模式概述

策略模式,即与解决问题的策略有关的模式,该模式旨在更好的实现策略的实现。策略模式分为三个部分:环境、抽象策略角色、具体策略角色。策略模式能使得更好地管理和使用一类算法。

  • 环境(context):用于确定对应的具体策略角色。并将解决策略的功能最终放在环境context中(只要向下调用功能即可)。
  • 抽象策略角色:能完成一种问题的解决的各种算法的范称,通常为接口,定义能解决问题的功能,但是不实现,具体的算法的步骤由实现类解决。
  • 具体策略角色:实现抽象策略角色,要完成具体的解决问题的功能的角色。

代码实现举例

如果一家商场能让顾客办会员卡以达到优惠的目的,而会员卡分为两种:八折卡、满减卡(每满三百减五十)。要求输入原价格和优惠方式,得到优惠后的价格。

分析,这里的抽象策略角色应该为优惠方式,而具体策略角色有三个:八折、满减、普通,而环境(通常用context表示)用于确定对应具体的优惠方式

抽象策略角色(优惠方式)

/**
 * 表示优惠的算法,具体优惠方式由实现类定义
 */
public interface Concession {
    //计算优惠后的价格的功能
    Double doConcession(Double price);
}

具体策略角色(优惠方式的不同具体实现)

普通:

/**
 * 没有优惠卡的计算方法
 */
public class NormalConcession implements Concession {
    @Override
    public Double doConcession(Double price) {
        return price;
    }
}

八折卡:

/**
 *  有八折卡的计算方法
 */
public class RebateConcession implements Concession {

    @Override
    public Double doConcession(Double price) {
        return price * 0.8;
    }
}

满减卡:

/**
 * 有满减卡的计算方法(每满三百减五十)
 */
public class ReturnConcession implements Concession {
    @Override
    public Double doConcession(Double price) {
        return price - ( (int)(price / 300) * 50 );
    }
}

普通策略模式的环境(context)

/**
 * 普通策略模式的Context类,用于决定不同的算法
 */
public class ConcessionContext {
    private Concession concession;
    public ConcessionContext(Concession concession){
        this.concession = concession;
    }
    public Double doConcession(Double price){
        return concession.doConcession(price);
    }
}

通过传入不同的具体策略模式的对象,来决定其成员变量concession是怎样的策略。并且将策略的功能最终放在环境中,业务代码只要调用环境的功能的方法即可。

主要业务代码

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入原价格");
        double price = scan.nextDouble();
        System.out.println("请输入优惠方式");
        String method = scan.next();
        ConcessionContext context = null;
        switch (method){
            case "普通":
                context = new ConcessionContext(new NormalConcession());
                break;
            case "八折卡":
                context = new ConcessionContext(new RebateConcession());
                break;
            case "满减卡":
                context = new ConcessionContext(new ReturnConcession());
                break;
            default:
                context = null;
                break;
        }
        if (context == null){
            throw new Exception("方式错误");
        }
        Double concession = context.doConcession(price);
        System.out.println("优惠后价格为:" + concession);
    }

但是这种方式有一些缺点:

  • 具体策略的选择在业务代码中进行,当修改底层时(比如增加、删除、更新了策略)也要修改业务代码
  • 业务代码中要实例化各种具体策略对象,使得代码繁杂,而且个人感觉这样使得环境context有点多余,在这种情况下,context只是起了一个没有中间作用的类,判断完后直接使用具体实现策略的对象调用方法差别也不大

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

结合的要点是,让环境context起到一个工厂类的效果,来完成实例化。并且,让context去完成具体实现策略的判断后再作对应,我们只要传入要选择的策略的标识即可(比如传入目标策略的名字,让context根据此名字来实例化对应的策略对象)。

升级版的context

/**
 * 策略者模式与简单工厂模式相结合
 */
public class ConcessionContextPlus {
    Concession context = null;
    public ConcessionContextPlus(String method) throws Exception {
        switch (method){
            case "普通":
                context = new NormalConcession();
                break;
            case "八折卡":
                context = new RebateConcession();
                break;
            case "满减卡":
                context = new ReturnConcession();
                break;
            default:
                context = null;
                break;
        }
        if (context == null) {
            throw new Exception("方式错误");
        }
    }
    public Double doConcession(Double price){
        return context.doConcession(price);
    }
}

升级后的业务代码

/**
 * 升级版业务(非常简洁,可读性高)
 */
public class MainPlus {
    public static void main(String[] args) throws Exception {
        Scanner scan = new Scanner(System.in);

        System.out.println("请输入原价格");
        double price = scan.nextDouble();

        System.out.println("请输入优惠方式");
        String method = scan.next();

        //实际上两步就完成了策略
        //传入优惠方式
        ConcessionContextPlus concessionContextPlus = new ConcessionContextPlus(method);
        //传入原价格
        Double concession = concessionContextPlus.doConcession(price);

        System.out.println("优惠后价格为" + concession);
    }
}

升级后的业务代码明显简单了很多。

总结

  • 策略模式能更好地管理和使用一类算法,在做算法增删改时,无需改动业务代码,只要对底层进行修改。使得代码的耦合度低,封装性强。
  • 策略模式结合简单工厂模式,让context起到工厂的作用,能让业务代码更为简洁,也更易于理解和使用。比起普通的策略模式,耦合度更低,封装性更强。