设计模式-策略模式

设计模式-策略模式

策略模式(Strategy Pattern)

策略模式基本概念(模式动机)

  • 在完成一项任务时,或是一件事的时候,会有多种不同的方式去完成。这每一种方式可以称为每一种策略。举个例子:现在要编写一个对税收进行计算的个人所得税计算系统:

    1
    2
    3
    4
    5
    6
    7
    首先:来列出税收的标准(纯属虚构)
    10000 起征 扣除 3%
    10000 ~ 20000 扣除 5%
    20000 ~ 30000 扣除 10%
    30000 ~ 40000 扣除 20%
    40000 ~ 50000 扣除 30%
    50000以上 扣除40%

    参上条件,先不要使用策略模式,进行最简单的实现。慢慢进行一个演变。(这里我用java代码来写)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package com.edu.design.strategy;

    /**
    * @ClassName SimpleStrategy
    * @Description 简单的税率计算类
    * @Author xnc
    * @Date 2019/1/13 11:06 PM
    * @return 1.0
    **/
    public class SimpleStrategy {
    /**
    * 简单实现税率的计算
    */
    public double count(double money){
    if(money>=50000){
    return money * 0.3;
    }else if(money>=40000){
    return money * 0.2;
    }else if(money>=30000){
    return money * 0.1;
    }else if(money>=20000){
    return money * 0.05;
    }else if(money>=10000){
    return money * 0.03;
    }
    return money;
    }
    public static void main(String[] args) {
    SimpleStrategy simpleStrategy = new SimpleStrategy();
    double tax = simpleStrategy.count(20000);
    System.out.println("税为:"+tax);
    }
    }

    现在,已经做出最简单的实现税率计算的代码。现在来分析一下这段代码的不足之处:

    • 假设现在业务场景变了,要增加对月收入6w - 7w进行税率计算,这样问题就来了,如果要增加新的计算则需要修改原来的代码。这就违反了java开发原则中的开闭原则。每次有新的业务时就需要修改代码,这样的代码是不健壮、不利于维护的。

设计模式只是一种思想,帮助我们编写出的代码是可重用的,健壮的、易于维护的。设计模式虽好,但如果业务场景不适用时,不要滥用。

模式结构

策略模式包含如下角色:

  • Context :环境类
  • Strategy : 抽象策略类
  • ConcreteStrategy : 具体策略类

Strategy

这是策略模式的类图,最近也在熟悉学习UML类图和时序图。(图是转载的)

  • 解读一下图,Context 这个类是主类,可以理解为策略算法调用者,就像 mian方法。类中有两个方法:

    1、algorithm 方法(算法),这个方法进行计算。

    2、setStrategy 方法(设置策略算法),这个方法set一个策略算法(Strategy)类给Context类,进行计算。

    类似 Spring 中的依赖注入,这里使用set 方法手动注入。

  • Strategy 类 (抽象策略算法类)

    1、这个类用于提供抽象的算法方法,可以是抽象类,也可以是接口。

    2、algorithm 方法,抽象的算法方法。

  • ConcreteStrategyA (具体算法类)

    1、algorithm 方法,具体的算法方法。

    这个类实现Strategy类中的抽象算法方法,并写出具体的实现,就是一个具体的算法类。可进行计算。

UML在平时工作中可能用到的比较少,但是作为程序员这是必须要学习的知识点

代码分析

就按照上面的 UML类图来设计代码,还是用 java 编写。(可能比较长)

编写环境类:Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.edu.design.strategy;
import com.edu.design.strategy.strategys.Strategy;

/**
* @ClassName Context
* @Description 环境类,用于抽象算法方法调用返回结果
* @Author xnc
* @Date 2019/1/194:06 PM
* @return 1.0
**/
public class Context {
/**
* 定义Strategy为属性
*/
private Strategy strategy;
/**
* 定义 set 方法
* @param strategy
*/
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
/**
* 调用 strategy的algorithm 进行计算
* @param money
* @return
*/
public double algorithm(double money){
return this.strategy.algorithm(money);
}
}

编写抽象的计算类:Strategy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.edu.design.strategy.strategys;

/**
* @ClassName Strategy
* @Description 抽象策略类
* @Author xnc
* @Date 2019/1/194:08 PM
* @return 1.0
**/
public abstract class Strategy {
/**
* 抽象的计算方法
* @param money 工资
*/
public abstract double algorithm(double money);
}

编写具体的实现类: ConcreteStrategy

ConcreteStrategyA类是算法1.0 ,就是开始的税率计算方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.edu.design.strategy.strategys;
/**
* @ClassName ConcreteStrategyA
* @Description 具体的算法实现
* @Author xnc
* @Date 2019/1/194:11 PM
* @return 1.0
**/
public class ConcreteStrategyA extends Strategy {
/**
* 具体的算法实现 税率算法 1.0 符合上面业务场景
* @param money 工资
*/
@Override
public double algorithm(double money) {
if(money>=50000){
return money * 0.3;
}else if(money>=40000){
return money * 0.2;
}else if(money>=30000){
return money * 0.1;
}else if(money>=20000){
return money * 0.05;
}else if(money>=10000){
return money * 0.03;
}
return money;
}
}

编写具体的实现类: ConcreteStrategy

ConcreteStrategyA类是算法2.0 ,按照新的税率标准所编写计算方法。(6w ~ 7w 税率计算)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.edu.design.strategy.strategys;
/**
* @ClassName ConcreteStrategyB
* @Description 具体的税率计算类
* @Author xnc
* @Date 2019/1/194:18 PM
* @return 1.0
**/
public class ConcreteStrategyB extends Strategy {
/**
* 税率计算 2.0
* @param money 工资
* @return
*/
@Override
public double algorithm(double money) {
if(money>=70000){
return money * 0.5;
}else if(money>=60000){
return money * 0.4;
}else if(money>=50000){
return money * 0.3;
}else if(money>=40000){
return money * 0.2;
}else if(money>=30000){
return money * 0.1;
}else if(money>=20000){
return money * 0.05;
}else if(money>=10000){
return money * 0.03;
}
return money;
}
}
  • 一共四个类,这就编写完了,可以看出来按照类图编写的代码,就是简单的策略模式。代码比较粗糙简单。可以看出来,现在我们的问题已近解决了,每当有新的税率计算方法时只需要增加一个新的算法实现类就可以了,不用去修改原有的代码。这样就很好的遵守了开闭原则。我写的这几段代码还是有严重问题的,如 if 判断语句过多,重复代码过多等问题。在这里我没有找到一个好的例子,暂时先写成这样了,望见谅。

优点

写完了代码,总结一下策略模式的优点:

  • 策略模式完美的遵循了开闭原则,使用户在不改变原来代码的基础上完成对不同算法的选择。同时可以灵活的增加各种算法。
  • 使用策略模式可以避免使用多重条件判断语句,提高代码可读性。

缺点

说完优点,接下来说说策略模式的缺点。

  • 客户端(调用方)需要知道所有的策略算法类,自行决定使用那个策略类。
  • 使用策略模式将会产生很多的策略算法类。