意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。(取缔if...else)
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。(比如在原锦囊妙计中增加一种新妙计,仅仅只需要增加一个“策略类”,即可在main方法中调用新妙计,符合开闭原则)
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。
StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。
创建一个接口。
Strategy.java
public interface Strategy {
public int doOperation(int num1, int num2);
}
步骤 2
创建实现接口的实体类。
OperationAdd.java
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
OperationSubstract.java
public class OperationSubstract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
OperationMultiply.java
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
创建 Context 类。
Context.java
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){ //这个executeStrategy名字可以更换为doOperation,品品更有趣
return strategy.doOperation(num1, num2);
}
}
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
StrategyPatternDemo.java
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
//如果我想新增一个除法,只需新建一个OperationDivision.java策略实现类,即可在main方法中使用除法了
}
}
执行程序,输出结果:
10 + 5 = 15 10 - 5 = 5 10 * 5 = 50
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.doOperation(10, 5));//这里的原executeStrategy名字可以更换为doOperation与
Strategy strategy = new OperationAdd();
System.out.println("10 + 5 = " + strategy.doOperation(10, 5));区别是什么?策略模式比一个接口多实现类直接调用有什么优势?是不是差不多!!!!!!
其实优势是解耦。
更多“相似设计模式”请移步博客:
https://www.cnblogs.com/imqsl/p/9540796.html
https://q395488499.iteye.com/blog/806398
策略模式的作用:就是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换。
策略模式的着重点:不是如何来实现算法,而是如何组织和调用这些算法,从而让我们的程序结构更加的灵活、可扩展。
我们前面的第一个报价管理的示例,发现每个策略算法实现对应的都是在QuoteManager 中quote方法中的if else语句里面,我们知道if else if语句里面的代码在执行的可能性方面可以说是平等的,你要么执行if,要么执行else,要么执行else if。
策略模式就是把各个平等的具体实现进行抽象、封装成为独立的算法类,然后通过上下文和具体的算法类来进行交互。各个策略算法都是平等的,地位是一样的,正是由于各个算法的平等性,所以它们才是可以相互替换的。虽然我们可以动态的切换各个策略,但是同一时刻只能使用一个策略。
在这个点上,我们举个历史上有名的故事作为示例:
三国刘备取西川时,谋士庞统给的上、中、下三个计策:
上策:挑选精兵,昼夜兼行直接偷袭成都,可以一举而定,此为上计计也。
中策:杨怀、高沛是蜀中名将,手下有精锐部队,而且据守关头,我们可以装作要回荆州,引他们轻骑来见,可就此将其擒杀,而后进兵成都,此为中计。
下策:退还白帝,连引荆州,慢慢进图益州,此为下计。
这三个计策都是取西川的计策,也就是攻取西川这个问题的具体的策略算法,刘备可以采用上策,可以采用中策,当然也可以采用下策,由此可见策略模式的各种具体的策略算法都是平等的,可以相互替换。
那谁来选择具体采用哪种计策(算法)?
在这个故事中当然是外部的客户端(刘备)选择使用某个具体的算法,
然后把该算法(计策)设置给到上下文(参谋部)当中;
还有一种情况就是客户端不选择具体的算法,把这个事交给上下文,这相当于刘备说我不管有哪些攻取西川的计策,我只要结果(成功的拿下西川),具体怎么攻占(有哪些计策,怎么选择)由参谋部来决定(上下文)。
1 //攻取西川的策略(接口) 2 public interface IOccupationStrategyWestOfSiChuan { 3 public void occupationWestOfSiChuan(String msg); 4 }
1 //攻取西川的上上计策(实现类一) 2 public class UpperStrategy implements IOccupationStrategyWestOfSiChuan { 3 @Override 4 public void occupationWestOfSiChuan(String msg) { 5 if (msg == null || msg.length() < 5) { 6 //故意设置障碍,导致上上计策失败 7 System.out.println("由于计划泄露,上上计策失败!"); 8 int i = 100/0; 9 } 10 System.out.println("挑选精兵,昼夜兼行直接偷袭成都,可以一举而定,此为上计计也!"); 11 } 12 }
1 //攻取西川的中计策(实现类二) 2 public class MiddleStrategy implements IOccupationStrategyWestOfSiChuan { 3 @Override 4 public void occupationWestOfSiChuan(String msg) { 5 System.out.println("杨怀、高沛是蜀中名将,手下有精锐部队,而且据守关头,我们可以装作要回荆州,引他们轻骑来见,可就此将其擒杀,而后进兵成都,此为中计。"); 6 } 7 }
1 //攻取西川的下计策(实现类三) 2 public class LowerStrategy implements IOccupationStrategyWestOfSiChuan { 3 @Override 4 public void occupationWestOfSiChuan(String msg) { 5 System.out.println("退还白帝,连引荆州,慢慢进图益州,此为下计。"); 6 } 7 }
1 //攻取西川参谋部,就是上下文啦,由上下文来选择具体的策略 2 public class OccupationContext { 3 4 public void occupationWestOfSichuan(String msg){ 5 //先用上上计策 6 IOccupationStrategyWestOfSiChuan strategy = new UpperStrategy(); 7 try { 8 strategy.occupationWestOfSiChuan(msg); 9 } catch (Exception e) { 10 //上上计策有问题行不通之后,用中计策 11 strategy = new MiddleStrategy(); 12 strategy.occupationWestOfSiChuan(msg); 13 } 14 } 15 }
1 //此时外部客户端相当于刘备了,不管具体采用什么计策,只要结果(成功的攻下西川) 2 public class Client { 3 4 public static void main(String[] args) { 5 OccupationContext context = new OccupationContext(); 6 //这个给手下的人激励不够啊 7 context.occupationWestOfSichuan("拿下西川"); 8 System.out.println("========================="); 9 //这个人人有赏,让士兵有动力啊 10 context.occupationWestOfSichuan("拿下西川之后,人人有赏!"); 11 } 12 }
控制台输出:
由于计划泄露,上上计策失败!
杨怀、高沛是蜀中名将,手下有精锐部队,而且据守关头,我们可以装作要回荆州,引他们轻骑来见,可就此将其擒杀,而后进兵成都,此为中计。
=========================
挑选精兵,昼夜兼行直接偷袭成都,可以一举而定,此为上计计也!
我们上面的策略接口采用的是接口的形式来定义的,其实这个策略接口,是广义上的接口,不是语言层面的interface,也可以是一个抽象类,如果多个算法具有公有的数据,则可以将策略接口设计为一个抽象类,把公共的东西放到抽象类里面去。
策略和上下文的关系:
在策略模式中,一般情况下都是上下文(Context)持有策略的引用(策略类的共有方法),以进行对具体策略的调用。
但具体的策略对象也可以从上下文中获取所需数据,可以将上下文当做参数传入到具体策略中,具体策略通过回调上下文中的方法来获取其所需要的数据。
文章节选自:http://www.runoob.com/design-pattern/strategy-pattern.html
https://www.cnblogs.com/lewis0077/p/5133812.html