今天给你们说说田忌赛马的故事。若有雷同,纯属巧合!话说在战国时期,群雄割据,硝烟四起,茶余饭后仍是少不了娱乐活动的,其中赛马是最火爆的。一天,孙膑看到田忌像个死鸡似的就知道确定赛马又输给了齐威王,立马抓住田忌去跟齐威王再赛一场。git
孙膑:“小忌啊,哥哥看着你心疼啊,哥哥出对策帮你赢一盘如何?”。github
田忌听到以后高兴得飞起,瞪大了两只金鱼眼“Really?只要能赢,我赴汤蹈火,以身相许又如何~”。算法
孙膑内心一万个草泥马在奔腾,差点没噎死本身“滚一边去,咱们这盘跟他show hand!”赛马开始,策略模式上场。此处应该有bgm“让咱们红尘做伴活得潇潇洒洒 策马奔腾共享人世繁华...呀啊呀啊,呀啊啊啊啊啊啊~”设计模式
定义一组算法,将每个算法封装起来,从而使它们能够相互切换。bash
1)一组算法,那就是不一样的策略。ide
2)这组算法都实现了相同的接口或者继承相同的抽象类,因此能够相互切换。post
策略模式涉及到的角色有三个:this
- 封装角色:上层访问策略的入口,它持有抽象策略角色的引用。spa
- 抽象策略角色:提供接口或者抽象类,定义策略组必须拥有的方法和属性。设计
- 具体策略角色:实现抽象策略,定义具体的算法逻辑。
在跟齐威王比赛以前来分析下以前输掉比赛的“策略”,首先来看封装角色,代码以下:
public class Context {
private Strategy strategy;
/**
* 传进的是一个具体的策略实例
* @param strategy
*/
public Context(Strategy strategy) {
this.strategy = strategy;
}
/**
* 调用策略
*/
public void contextInterface() {
strategy.algorithmLogic();
}
}复制代码
Context持有Strategy的引用,而且提供了调用策略的方法,很清晰。
再来抽象策略角色,定义了策略组的方法,代码以下:
public interface Strategy {
public void algorithmLogic();
}复制代码
输掉比赛的“策略”也是一种策略,是具体策略角色类,来看代码:
public class ConcreteStrategyA implements Strategy{
@Override
public void algorithmLogic() {
// 具体的算法逻辑(输了比赛)
System.out.println("第一场:上等马vs上等马 第二场:中等马vs中等马 第三场:下等马vs下等马 赛果:输!");
}
}复制代码
看到这里,孙膑一阵无语,惨不忍睹也得看结果的,客户端代码以下:
public class Client {
public static void main(String[] args) {
// 操控比赛,这场要输
Context context = new Context(new ConcreteStrategyA());
context.contextInterface();
}
}复制代码
两句代码,传入具体策略对象,调用策略入口方法,运行结果以下:
第一场:上等马vs上等马 第二场:中等马vs中等马 第三场:下等马vs下等马 赛果:输!
田忌跟孙膑说:“膑哥,我怕!”,孙膑:“不用怕,哥哥在!”。
田忌找到齐威王“大王,咱们再...再再来一盘,输了请吃饭”
瞅瞅孙膑出的策略,一睹军事家的风采,“赢”的具体策略类代码以下:
public class ConcreteStrategyB implements Strategy{
@Override
public void algorithmLogic() {
// 赢
System.out.println("第一场:下等马vs上等马 第二场:上等马vs中等马 第三场:中等马vs下等马 赛果:赢!");
}
}复制代码
再来看客户端的代码:
public class Client {
public static void main(String[] args) {
// 操控比赛,这场要赢,哈哈哈
Context context = new Context(new ConcreteStrategyB());
context.contextInterface();
}
}复制代码
运行结果以下:
第一场:下等马vs上等马 第二场:上等马vs中等马 第三场:中等马vs下等马 赛果:赢!
田忌拍烂手掌,重要的是今天晚饭有着落了,还要对膑哥哥以身相许的......
1)良好的扩展性。增长一种策略,只要实现接口,写上具体逻辑就能够了。当旧策略不须要时,直接剔除就行。
2)良好的封装性。策略的入口封装在Context封装类中,客户端只要知道使用哪一种策略就传哪一种策略对象就能够了。
3)避免了像简单工厂模式这样的多重条件判断。
1)客户端必须了解策略组的各个策略,而且决定使用哪个策略,也就是各个策略须要暴露给客户端。
2)若是策略增多,策略类的数量就会增长。
上面说到策略模式有一个缺点,就是全部的策略都必须暴露出去,让客户端自行选择策略使用。如今来改善这一缺陷,而改善这个缺陷须要跟简单工厂模式结合混编,继续往下看。
固然,军事家孙膑也会想到这一点,怎么可能会把本身的套路全都暴露给别人呢,那还怎么玩是吧。不过,历史上并无说孙膑改善了这点,如今是我来改善这个缺陷,哈哈哈~
思考一个问题,策略暴露了,改善就是把策略隐藏起来,而工厂模式就有这个效果,客户端不须要知道策略具体是什么,只知道结果就好。OK,那么咱们可使用工厂模式把策略当作产品生成吗?答案是确定的。策略模式的入口就在Context封装类,能够从这个角色作手脚。先看代码:
public class Context {
private Strategy strategy;
// 把建立策略放在封装角色内,客户端只须要知道结果
public void factory(String strategyType) {
if (strategyType.equals("WIN")) {
strategy = new ConcreteStrategyB();
} else if (strategyType.equals("LOSE")) {
strategy = new ConcreteStrategyA();
}
}
/**
* 调用策略
*/
public void contextInterface() {
strategy.algorithmLogic();
}
}复制代码
代码很简单,增长了factory的方法,这个方法做用就是建立策略对象。这样,客户端就不须要去理解具体的策略,只需知道具体策略的结果就好。看看客户端代码:
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.factory("LOSE");
context.contextInterface();
}
}复制代码
注意策略模式和工厂方法模式的区别,在前面工厂方法模式中有说到,这里就再也不阐述。策略模式自己也相对比较简单,重点在它的扩展以及其它模式的对比,分析各自的优缺点。来看看策略工厂这样的模式存在缺点吗?很明显,若是须要添加或者淘汰一种策略,Context就必须修改,这并不符合开闭原则。在《设计模式之禅》中的提出经过策略枚举和反射机制对策略模式进行改良,膜拜了~可是要添加或淘汰策略,仍是得去对枚举进行修改,也不符合开闭原则。根据本身项目状况,选择最适合本身项目的模式。下一篇是责任链模式,欢迎继续关注,goodbye!
设计模式Java源码GitHub下载:https://github.com/jetLee92/DesignPattern