策略模式(Strategy)定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的优点:

1、多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。

2、策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。

3、策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。

4、策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。

5、策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

 

环境类:

 1 public class Context {
 2     private Strategy strategy;
 3 
 4     public Strategy getStrategy() {
 5         return strategy;
 6     }
 7 
 8     public void setStrategy(Strategy strategy) {
 9         this.strategy = strategy;
10     }
11 
12     public void strategyMethod() {
13         strategy.strategyMethod();
14     }
15 }

 

策略类:

 1 public interface Strategy {
 2     public void strategyMethod();
 3 }
 4 
 5 public class ConcreteStrategyA implements Strategy {
 6     @Override
 7     public void strategyMethod() {
 8         // TODO Auto-generated method stub
 9         System.out.println("策略A");
10     }
11 }
12 
13 public class ConcreteStrategyB implements Strategy {
14     @Override
15     public void strategyMethod() {
16         // TODO Auto-generated method stub
17         System.out.println("策略B");
18     }
19 }

 

调用方式:

 1 public class Client {
 2     /*
 3      * 例子:通用类库DBHelper
 4      * 无论连接SQLServer/Oracel/MySql数据库
 5      * 不同的对象,具有相同的使用方式Context
 6      */
 7     public static void main(String[] args) {
 8         //多个类有相似的功能,客户将具体的类传入Context,调用Context统一的方法实现具体功能
 9         Strategy strategy;//扩展点
10         
11         //使用策略A
12         strategy = new ConcreteStrategyA();
13         
14         //扩展点(封装了变化点)->切换策略B
15         //strategy = new ConcreteStrategyB();
16         
17         //稳定代码
18         Context context = new Context();
19         context.setStrategy(strategy);
20         context.strategyMethod();
21     }    
22 }

执行结果:

 

策略模式经常用于消除多条件选择的代码,例如如下代码:

 1 public class StrategyDemo {
 2     public void NoStrategyMethod(Integer type) {
 3         switch(type) {
 4             case 1:
 5                 System.out.println("执行第一种策略");
 6                 break;
 7             case 2:
 8                 System.out.println("执行第二种策略");
 9                 break;
10             case 3:
11                 System.out.println("执行第三种策略");
12                 break;
13             default:
14                 System.out.println("没有这种策略");
15                 break;
16         }
17     }
18     
19     public static void main(String[] args) {
20         // TODO Auto-generated method stub
21         Integer type = 1;
22         StrategyDemo demo = new StrategyDemo();
23         demo.NoStrategyMethod(type);        
24     }
25 }

方法NoStrategyMethod(),中根据参数type的值,执行不同的逻辑,这种写法是典型的策略模式的应用场景。

如果程序的需求改变,需要增加新的type,例如type值可以为4、5、6……或者某些已有的type值得处理逻辑需要修改。

如何既能满足这些变化,又不需要修改StrategyDemo类的代码或者尽量减少对Strategy类的影响呢?

使用设计模式就是通过扩展而不是修改的方式来满足需求的变化,或者将这种修改转移到别类中。下面给出策略模式的解决方案。

 

首先定义以下接口和类,用于管理策略:

 1 package com.example.uml;
 2 
 3 public interface IType {
 4     public void setStrategy(Strategy strategy);
 5     
 6     public void UseStrategy();
 7 }
 8 
 9 class MyType implements IType{
10     
11     private Strategy strategy;
12     
13     @Override
14     public void setStrategy(Strategy strategy) {
15         this.strategy = strategy;        
16     }
17     
18     @Override
19     public void UseStrategy() {
20         this.strategy.StrategyMethod();
21     }    
22 }
23 
24 
25 abstract class Strategy{
26     public abstract void StrategyMethod();
27 }
28 
29 class Strategy1 extends Strategy{
30 
31     @Override
32     public void StrategyMethod() {
33         System.out.println("执行第一种策略");
34     }
35 }
36 
37 class Strategy2 extends Strategy{
38 
39     @Override
40     public void StrategyMethod() {        
41         System.out.println("执行第二种策略");
42         System.out.println("第二种策略修改了");
43     }
44 }
45 
46 class Strategy3 extends Strategy{
47 
48     @Override
49     public void StrategyMethod() {
50         System.out.println("执行第三种策略");
51     }
52 }
53 
54 class Strategy4 extends Strategy{
55 
56     @Override
57     public void StrategyMethod() {
58         System.out.println("执行第四种策略");
59     }
60 }

 

客户端调用方式发生修改:

 1 package com.example.uml;
 2 
 3 public class StrategyDemo {
 4     @Deprecated
 5     public void NoStrategyMethod(Integer type) {
 6         switch (type) {
 7         case 1:
 8             System.out.println("执行第一种策略");
 9             break;
10         case 2:
11             System.out.println("执行第二种策略");
12             break;
13         case 3:
14             System.out.println("执行第三种策略");
15             break;
16         default:
17             System.out.println("没有这种策略");
18             break;
19         }
20     }
21 
22     public void UseStrategyMethod(IType type) {
23         type.UseStrategy();
24     }
25 
26     public static void main(String[] args) {
27         StrategyDemo demo = new StrategyDemo();
28 
29         // 不使用策略模式
30 //        Integer type = 1;
31 //        demo.NoStrategyMethod(type);
32 
33 //        System.out.println("-----------------------------------");
34 
35         // 使用策略模式
36         IType myType = new MyType();
37 
38         // 使用新增的策略4,无需修改当前类的具体业务逻辑,只需要修改new的类即可
39         Strategy strategy4 = new Strategy4();
40         myType.setStrategy(strategy4);
41         myType.UseStrategy();
42 
43         System.out.println("-----------------------------------");
44 
45         // 假如策略2的具体逻辑发生变化,则当前类无需改变
46         Strategy strategy2 = new Strategy2();
47         myType.setStrategy(strategy2);
48         myType.UseStrategy();
49     }
50 }

现在的需求发生变化,第二种策略的处理逻辑改变了(增加了一行输出),并且增加了第四种的策略需要处理。

一种方案时修改第5行的方法,当然这可以完成这个需求,而且可以说是简单而直接。

但如果这个逻辑很复杂,而又经常变化,这种修改方式就有可能引入Bug,或者影响到原本正常的功能,。

因此可以实通过IType接口与Strategy抽象类,将变化点与客户端隔离。也就是将可能频繁变化的代码从当前类中抽离。

StrategyDemo类就是客户端,这个类中不再包含判断逻辑,也不包含策略的具体逻辑,只负责调用策略类的方法。

在main()方法中,只需要new出对应的实例,然后执行抽象的方法即可,具体的内容,都有MyType、Strategy1、Strategy2、Strategy3、Strategy4……这些类来处理。在新增策略或策略改变而必须修改代码时,对main()方法影响减少到最小。

 

posted on 2019-12-28 11:10  Sempron2800+  阅读(231)  评论(0编辑  收藏  举报