策略模式

baike 2020-06-14

概念:

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,次模式让算法的变化独立于使用算法的客户。

很多同学可能不太理解这个定义,我举个简单的例子,比如客户要实现一个功能,分为A,B,C,D4个步骤完成,其中A和C步骤是很容易发生改变的,所以我们将A和C步骤分别封装起来,

具体实现方式是将A和C定义为接口,各自分别有1,2,3种实现方式。此时A和C对应的就是算法族,各自的实现叫做算法,各个算法之间可以相互替换,来达到不同的实现目的。客户

需要实现的功能可以由A和C算法族组合实现各式各样不同的功能,而我们实现功能的主干永远不会改变。

设计原则:

上面的小例子也用到了几个设计原则:

1,将程序中可能需要改变的部分独立出来,不要和不需要变化的代码放在一起。

2,针对接口编程,而不是针对实现编程。

3,少用继承,多用组合。

第1点相信大家都能理解,但是在实际工作中,要完美的运用实际上并没有那么简单。

第2点,先说说针对接口编程的好处,就比如例子中,主干功能代码不需要任何改变,我们就能实现各种不同功能的实现,精髓就在各个具体的算法实现了一个接口,我们只需要传入不同的具体的实现

就能轻松实现不同的功能。如果你不理解为什么能这样做,那你可能还不理解java的多态,父类引用指向子类对象。我们也可以通过这个例子来反向理解多态,你会发现原来多态就是这样,so easy!

第3点,少用继承,多用组合。继承太过于笨重,如果我继承某个接口或者类,我就必须实现这个接口或者类的所有方法,这显然是不合理的,组合就不一样了,我可以任意组合实现各种各样的功能。

比如A的一种具体实现和C的一种具体实现就是一类组合,这里我们可以实现3x3=9种组合,也就意味着有9种实现该功能的方式,如果需要增加,我们只需要增加一种实现类,而不需要修改主干的任意

代码就能轻易实现。

代码实现:

这里举一个简单的小例子,比如某明显开一场演唱会,后台调音师需要根据明星演唱的歌曲准备背景音乐。这里就可以运用策略模式去实现。

1,先定义一个背景音乐的接口,创建一个播放的方法。

package strategy;

/**
 * @ClassName BackgroundMusic
 * @Description 背景音乐接口
 * @Author liuyi
 * @Date 2020/6/14 11:08
 * @Version 1.0
 */
public interface BackgroundMusic {
    /**
     * @Author liuyi
     * @Description 定义播放的方法
     * @Date 11:10 2020/6/14
     * @Param []
     * @return void
     **/
    public void play();
}

2,分别有三种背景音乐去实现该接口

背景音乐1

package strategy;

/**
 * @ClassName BackgroundMusic1
 * @Description TODO
 * @Author liuyi
 * @Date 2020/6/14 11:12
 * @Version 1.0
 */
public class BackgroundMusic1 implements BackgroundMusic {
    @Override
    public void play() {
        System.out.println("开始播放背景音乐1");
    }
}

背景音乐2

package strategy;

/**
 * @ClassName BackgroundMusic2
 * @Description TODO
 * @Author liuyi
 * @Date 2020/6/14 11:13
 * @Version 1.0
 */
public class BackgroundMusic2 implements BackgroundMusic{
    @Override
    public void play() {
        System.out.println("开始播放背景音乐2");
    }
}

背景音乐3

package strategy;

/**
 * @ClassName BackgroundMusic3
 * @Description TODO
 * @Author liuyi
 * @Date 2020/6/14 11:13
 * @Version 1.0
 */
public class BackgroundMusic3 implements BackgroundMusic {
    @Override
    public void play() {
        System.out.println("开始播放背景音乐3");
    }
}

3,创建背景音乐编号枚举类

package strategy;

/**
 * @ClassName MusicNumEnum
 * @Description 背景音乐编号枚举类
 * @Author liuyi
 * @Date 2020/6/14 11:20
 * @Version 1.0
 */
public enum MusicNumEnum {
    music1(1,"strategy.BackgroundMusic1"),
    music2(2,"strategy.BackgroundMusic2"),
    music3(3,"strategy.BackgroundMusic3");

    MusicNumEnum(int num, String className){
        this.num = num;
        this.className = className;
    }
    //编号
    private int num;
    //类名称
    private String className;

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }
    //根据num获取className
    public static String getClassNameByNum(int num){
        MusicNumEnum[] values = MusicNumEnum.values();
        for (MusicNumEnum value : values) {
            if(value.getNum()==num){
                return value.getClassName();
            }
        }

        return null;
    }

}

4,测试

package strategy;

/**
 * @ClassName StrategyTest
 * @Description 策略模式测试类
 * @Author liuyi
 * @Date 2020/6/14 11:16
 * @Version 1.0
 */
public class StrategyTest {
    public static void main(String[] args) throws Exception {
        palyByNum(1);
        palyByNum(3);
        palyByNum(2);
    }
    /**
     * @Author liuyi
     * @Description 根据播放编号播放对应的背景音乐
     * @Date 11:41 2020/6/14
     * @Param [num]
     * @return void
     **/
    public static void palyByNum(int num) throws Exception{
        //根据背景音乐编号播放对应的背景音乐
        BackgroundMusic backgroundMusic = (BackgroundMusic)(Class.forName(MusicNumEnum.getClassNameByNum(num)).newInstance());
        backgroundMusic.play();
    }
}

从代码可以看出我们需要播放某个因为,只需要传入对应的编号即可,不需要对主干代码进行修改。如果下场演唱会需要新增背景音乐,则添加一个背景音乐类,然后在枚举类配置对应的编号和类的关系即可。

从策略模式的概念来看,每一种背景音乐其实就是一种具体的算法,背景音乐1,背景音乐2,背景音乐3它们直接可以互相替换,来达到不同的目的,而使用的客户是不需要知道这些的存在的, 用户只需要传入算法

对应的编号或者类型即可。

相关推荐