设计模式设计模式

设计模式--策略模式

2017-11-28  本文已影响83人  w1992wishes

目录

本文的结构如下:

一、引言

写这篇文章的时间是17年11月18号上午9点半,NBA正打得火热,骑士VS76人,詹韦对阵大帝和西帝,内线孱弱的骑士被大帝蹂躏,欲生欲死;再看火箭VS篮网,第一节哈登7中7,三分5中5,狂砍20分;又有步行者VS魔术,奥拉迪波10中10......可谓精彩。

我们的话题由此展开。

篮球而言,对阵双方4节比赛结束后,哪队分数高,哪队获胜,打平加时继续较量。所以,说到最后,篮球是一个得分的比赛,而得分的方式又有许多种,库日天的三分,老詹的上篮,德罗赞的中投,哈登的罚球(我不是黑,不要喷我,只是选择比较有代表性的得分方式)。具体的得分方式根据防守而定,篮下空无一人果断暴扣,三分线外没人防守,三分扔起,当然老汉日常碧浪就不说了......

在软件开发中,也有很多类似的场景,实现某一个功能有多条途径,每一条途径对应一种算法,此时可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。这就是策略模式。

二、什么是策略模式

在策略模式中,定义了一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。

策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“依赖倒转原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。

策略模式定义如下:

策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。

三、模式的结构

策略模式的UML类图如下:

20171128_strategy01.png

在策略模式结构图中包含如下几个角色:

四、典型代码

策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。环境类是需要使用算法的类。在一个系统中可以存在多个环境类,它们可能需要重用一些相同的算法。

在使用策略模式时,首先应该创建一个抽象策略类,其典型代码如下所示:

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)算法的实现方案。

五、代码示例

这里以一个球员上场打球为例说明。

5.1、不用策略模式

设计一个球员类:

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("------------------------");
    }
}

这段代码实现了根据不同防守得分的方式,防守方式不同,直接修改客户端的参数,而不需修改源码,但还是有一些问题:

5.1、使用策略模式

重构后Basket充当抽象策略类,FreeThrows、SlamDunk和ThreePointer充当具体策略类,Player充当环境类。

20171128_strategy02.png

代码如下:

策略类:

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的,封装的算法是任意给用户使用,而模板方法模式中的虚方法更多的是有限制的,一般不希望被外部调用,而是在模板方法中在固定的顺序位置被调用。

七、优点和缺点

7.1、优点

策略模式的主要优点如下:

7.2、缺点

策略模式的主要缺点如下:

八、适用环境

在以下情况下可以考虑使用策略模式:

九、模式应用

Java SE的容器布局管理就是策略模式的一个经典应用实例,其基本结构如下图:

20171128_strategy03.png
上一篇下一篇

猜你喜欢

热点阅读