java设计模式之策略模式

介绍

  • 意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  • 解决问题: 在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护(简而言之消除if分支过多问题)
  • 指导思想
    开闭原则、迪米特法则、依赖倒置原则

问题场景

公司有节日活动,为每个员工分发奖金。经理(及以上)发5000,普通员工2000。


不使用策略模式

        // 经理
        String manage = "manage";
// 普通员工
        String clerk = "clerk";
        Scanner scanner = new Scanner(System.in);
        String input = scanner.next();
        if(input.equals(manage)){
            System.out.println("经理,得5000奖金");
        }else if(input.equals(clerk)){
            System.out.println("其他员工,2000元奖金");
        }

这样做把具体的活动奖金分发算法暴露给调用方,造成维护困难。

此时老板说,活动不发奖金了,改成聚餐,送购物卡等等。你要再修改复杂的逻辑。

第一次优化版

针对上述问题,活动奖金分发算法不再暴露给调用方,封装到统一的接口。并编写对应实现类,代码如下:

  1. 奖金分发策略接口
public interface BonusStrategy{
    /**
     * 奖金分发具体策略
     */
    void bonusDistribution();
}
  1. 一等奖(经理级以上)具体奖励策略
public class FirstPrize implements BonusStrategy{
    @Override
    public void bonusDistribution(){
        System.out.println("经理,一等奖,5000元");
    }
}

3 .二等奖(普通员工)具体奖励策略

public class FirstPrize implements BonusStrategy{
    @Override
    public void bonusDistribution(){
        System.out.println("经理,一等奖,5000元");
    }
}
  1. 调用方代码:
    public static void main(String[] args){
// 经理
        String manage = "manage";
        String clerk = "clerk";

        Scanner scanner = new Scanner(System.in);
        BonusStrategy bonusStrategy = null;
        System.out.println("输入你的岗位");
        String input = scanner.next();
        if(input.equals(manage)){
            bonusStrategy = new FirstPrize();
        }else if(input.equals(clerk)){
            bonusStrategy = new SecondPrize();
        }
        bonusStrategy.bonusDistribution();
    }
  • 此时老板发炎,经理不再是奖励5000元了,是送一辆车,只需要更改 FirstPrize.java即可,调用方代码无需更改。
  • but,老板又发炎了,在经理之上有新增一档特等奖,用于奖励特别优秀的经理,奖金一万。。此时代码如下。
    1. 新增一个特等奖策略,并实现奖金策略接口
public class GrandPrize implements BonusStrategy{
    @Override
    public void bonusDistribution(){
        System.out.println("特等奖,奖励一万");
    }
}
  1. 调用方也要做修改,主要新增一个if分支
public static void main(String[] args){
// 经理
        String manage = "manage";
        String clerk = "clerk";
        String greatGod = "greatGod";

        Scanner scanner = new Scanner(System.in);
        BonusStrategy bonusStrategy = null;
        System.out.println("输入你的岗位");
        String input = scanner.next();
        if(input.equals(manage)){
            bonusStrategy = new FirstPrize();
        }else if(input.equals(clerk)){
            bonusStrategy = new SecondPrize();
        }else if(input.equals(greatGod)){
            bonusStrategy = new GrandPrize();
        }
        bonusStrategy.bonusDistribution();
    }

* 此时我们改动了调用方代码,添加if分支,耦合度过高。

第二次优化版

上述方法已经实现了算法内容的透明性,一定程度上实现了解耦,但策略数量增加时,客户端还是要添加if分支实现相应的修改。

  1. 创建静态工厂

    如果具体策略实现类交由客户端指定,那么if分支就一直会存在,最好的办法是结合静态工厂,把对象创建交由工厂类来完成。

  static final Map<String,BonusStrategy> STRATEGY_MAP = new HashMap();
    static {
        STRATEGY_MAP.put("manage",new FirstPrize());
        STRATEGY_MAP.put("clerk",new SecondPrize());
        STRATEGY_MAP.put("greatGod",new GrandPrize());
    }
    /**
     * 获取具体策略
     * @param post
     * @return
     */
    public static BonusStrategy getStrategy(String post){
        return STRATEGY_MAP.get(post);
    }
  1. 消灭客户端(调用方)的if分支
   public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        BonusStrategy bonusStrategy = null;
        System.out.println("输入你的岗位");
        String input = scanner.next();
        BonusStrategy strategy = StrategyFactory.getStrategy(input);
        strategy.bonusDistribution();
    }

此时,如果业务有变,我们的系统会如何应对呢?

  1. 修改奖金分发算法:只需修改对应的策略实现类。
  2. 增加奖金分发策略:新增一个策略实现策略接口,修改静态工厂在map添加新策略。

两种情况客户端代码都不用更改,也不存在if分支。

最终版

上述方法已经最大程度的降低耦合度,但还是有瑕疵,工厂接收的参数是string类型,存在硬编码问题。可以进一步优化。

总结

  1. 策略模式主要目的是封装算法,使之对客户端透明,消灭if分支。降低程序耦合度。
  2. 策略模式很少单独使用,一般结合工厂模式、享元模式等。




posted @ 2019-01-18 10:07  树荫下的天空  阅读(921)  评论(0编辑  收藏  举报