@w1992wishes
2018-03-13T14:32:15.000000Z
字数 5334
阅读 859
设计模式
行为型模式
本文的结构如下:
写这篇文章的时间是17年11月18号上午9点半,NBA正打得火热,骑士VS76人,詹韦对阵大帝和西帝,内线孱弱的骑士被大帝蹂躏,欲生欲死;再看火箭VS篮网,第一节哈登7中7,三分5中5,狂砍20分;又有步行者VS魔术,奥拉迪波10中10......可谓精彩。
我们的话题由此展开。
篮球而言,对阵双方4节比赛结束后,哪队分数高,哪队获胜,打平加时继续较量。所以,说到最后,篮球是一个得分的比赛,而得分的方式又有许多种,库日天的三分,老詹的上篮,德罗赞的中投,哈登的罚球(我不是黑,不要喷我,只是选择比较有代表性的得分方式)。具体的得分方式根据防守而定,篮下空无一人果断暴扣,三分线外没人防守,三分扔起,当然老汉日常碧浪就不说了......
在软件开发中,也有很多类似的场景,实现某一个功能有多条途径,每一条途径对应一种算法,此时可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。这就是策略模式。
在策略模式中,定义了一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。
策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“依赖倒转原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。
策略模式定义如下:
策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。
策略模式的UML类图如下:
在策略模式结构图中包含如下几个角色:
策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。环境类是需要使用算法的类。在一个系统中可以存在多个环境类,它们可能需要重用一些相同的算法。
在使用策略模式时,首先应该创建一个抽象策略类,其典型代码如下所示:
public abstract class AbstractStrategy {
public abstract void algorithm();
}
封装每一种具体算法的类作为该抽象策略类的子类,期典型代码如下:
public class ConcreteStrategyA extends AbstractStrategy {
public void algorithm() {
//todo
}
}
public class ConcreteStrategyB extends AbstractStrategy {
public void algorithm() {
//todo
}
}
在Context类与抽象策略类之间建立一个关联关系,其典型代码如下:
public class Context {
private AbstractStrategy strategy;
public Context(AbstractStrategy strategy){
this.strategy = strategy;
}
public void algorithm(){
strategy.algorithm();
}
}
客户端代码如下:
public class Client {
public static void main(String[] args) {
AbstractStrategy strategy = new ConcreteStrategyA();
Context context = new Context(strategy);
context.algorithm();
}
}
也可以将具体策略类类名存储在配置文件中,通过反射来动态创建具体策略对象,从而使得用户可以灵活地更换具体策略类,增加新的具体策略类也很方便。策略模式提供了一种可插入式(Pluggable)算法的实现方案。
这里以一个球员上场打球为例说明。
设计一个球员类:
public class Player {
private String defensive;//对手的防守情况
public Player(){
}
public void setDefensive(String defensive){
this.defensive = defensive;
}
public void score(){
if ("undefended".equalsIgnoreCase(defensive)){
System.out.println("篮下无人防守,他起飞,大风车战斧扣篮,但是扣飞了,得0分,what a pity!");
}else if("foul".equalsIgnoreCase(defensive)){
System.out.println("他身上挂着三个人,强起上篮命中,哨响了,and one,他走上罚球线,加罚不中,得两分");
}else if("1s".equalsIgnoreCase(defensive)){
System.out.println("比赛剩下最后1s,还落后2分,现在比赛回来,他接到了球,没时间了,后撤步直接射三分,有没有?命中了,绝杀,绝杀。");
}
}
}
客户端测试:
public class Client {
public static void main(String[] args) {
Player player = new Player();
String defensive;//对手防守情况
player.setDefensive("undefended");
player.score();
System.out.println("------------------------");
player.setDefensive("foul");
player.score();
System.out.println("------------------------");
player.setDefensive("1s");
player.score();
System.out.println("------------------------");
}
}
这段代码实现了根据不同防守得分的方式,防守方式不同,直接修改客户端的参数,而不需修改源码,但还是有一些问题:
重构后Basket充当抽象策略类,FreeThrows、SlamDunk和ThreePointer充当具体策略类,Player充当环境类。
代码如下:
策略类:
public abstract class Basket {
public abstract void score();
}
public class SlamDunk extends Basket{
public void score() {
System.out.println("篮下无人防守,他起飞,大风车战斧扣篮,但是扣飞了,得0分,what a pity!");
}
}
public class FreeThrows extends Basket{
public void score() {
System.out.println("他身上挂着三个人,强起上篮命中,哨响了,and one,他走上罚球线,加罚不中,得两分");
}
}
public class ThreePointer extends Basket {
public void score() {
System.out.println("比赛剩下最后1s,还落后2分,现在比赛回来,他接到了球,没时间了,后撤步直接射三分,有没有?命中了,绝杀,绝杀。");
}
}
环境类:
public class Player {
private Basket basket;
public Player(Basket basket){
this.basket = basket;
}
public void score(){
basket.score();
}
}
客户端:
public class Client {
public static void main(String[] args) {
Basket basket = new ThreePointer();
Player player = new Player(basket);
player.score();
}
}
重构后的代码,如果需要增加新的得分方式,原有代码均无须修改,只要增加一个新的得分类作为抽象得分类的子类,实现在抽象得分类中声明的得分方法,然后可以通过配置文件的方式更换具体子类,完全符合“开闭原则”。
Template Method模式是将相同的算法放在一个类中,将算法变化的部分放在子类中实现,策略模式是将不同的算法用不同的策略类表示,似乎没有太大的区别。
认真想想还是有区别的。
策略模式的策略类中的方法一般是public的,封装的算法是任意给用户使用,而模板方法模式中的虚方法更多的是有限制的,一般不希望被外部调用,而是在模板方法中在固定的顺序位置被调用。
策略模式的主要优点如下:
策略模式的主要缺点如下:
在以下情况下可以考虑使用策略模式:
Java SE的容器布局管理就是策略模式的一个经典应用实例,其基本结构如下图: