Back
Featured image of post 设计模式-策略模式

设计模式-策略模式

一、模式简介

《HeadFirst设计模式》中讲解的第一个设计模式就是策略模式,策略模式官方给出的定义:它是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。 我认为学好设计模式最重要的就是理解模式的含义和模式的应用场景。在没有熟悉模式的含义之前我们可能会觉得定义有些抽象,下面我会对模式的含义和应用场景进行详细的说明,回过头来再看它的定义相信你会豁然开朗。

二、模式详解

想象一下有这样的一个场景,现在让你设计一个游戏有这个游戏中有骑兵( Cavalry)、步兵(Infantry)、怪兽(Monster),这些角色可以行走(Walk)和交谈(exchange)我们会怎样设计?我们可能会想到定义一个角色类,类中有Walk和exchange方法,因行走和交谈是共有的行为不同的角色只需要继承角色类去即可。就有如下的结构:

下面因为某种原因要该游戏要加入战斗的功能,并且每个角色战斗方式不同。那么我们可能想只要在角色类中加入一个抽象战斗方法,子类去重写它不就可以了吗?但是试想一下如果我们加入了一个弱女子角色呢?弱女子也会去重写角色的战斗方法这明显是不符合常理的,虽然我们可以在重写方法时什么也不做,但是我们后续加入大量的并没有战斗功能的角色或者再加一个抽象的方法,每次都还要重写很多方法。 此时我们又想到了一个办法,我们可以观察到角色的行走和交谈是不变的,可能改变的只是战斗功能,把战斗功能变成一个接口,如果需要使用战斗功能的角色实现战斗方法就可以完成。

可能这么写我们会觉得很合理,但是仔细想当拥有大量的战斗角色时,是不是都要实现这个战斗接口,并没有达到代码的重复利用。这个时候应该怎么办呢?我们可以用一些特定的战斗技能类去实现我们的战斗接口,然后在不同的角色中实例化战斗接口中具体想要使用的特定战斗技能,这样既可以达到代码的复用,也可以根据自身需要去切换不同的战斗技能。

那么这就是我们的策略模式,观察这张图我们会发现假如把角色当成”高层“而技能当作”底层“,那么这么一看是否正贴切的符合我们的依赖倒置原则

三、代码示例

  • 战斗接口(策略接口)
public interface Fight {
    /**
     * 战斗
     */
    void fight();
}
  • 使用魔法战斗(策略类)
public class MagicFight implements Fight{
    @Override
    public void fight() {
        System.out.println("使用魔法战斗");
    }
}
  • 使用物理战斗(策略类)
public class PhysicsFight implements Fight{
    @Override
    public void fight() {
        System.out.println("使用物理战斗");
    }
}
  • 角色类
/**
 * 角色
 *
 */
public abstract class Role {

    Fight fight;

    /**
     * 提供设置器,可在运行时切换
     *
     */
    void setFight(Fight fg){
        fight=fg;
    }
    /**
     * 行走
     */
    void walk() {
        System.out.println("------行走");
    }

    /**
     * 交流
     */
    void exchange(){
        System.out.println("-------交流");
    }

    /**
     * 战斗
     */
    void fight(){
        if (fight!=null){
            fight.fight();//角色对象不处理战斗行为,委派给战斗接口
        }
    }
}

  • 骑士
/**
 * 骑兵
 *
 */
public class Cavalry extends Role{
    public Cavalry(){
        fight=new PhysicsFight();
    }

}
  • 怪物
/**
 * 怪物
 *
 */
public class Monster extends Role{
    public Monster(){
        fight=new MagicFight();
    }
}

四、适用场景

  • 当你想使用对象中各种不同的算法变体,并希望能在运行时切换算法时,可使用策略模式

​ 此例中可将战斗行为看作一族算法,不同方式的攻击看做算法变体,通过set方法可以运行时切换。

  • 当你有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式 。

​ 不同角色之间在战斗行为上略有不同,代码复用。

  • 如果算法在上下文的逻辑中不是特别重要,使用该模式能将类的业务逻辑与其算法实现细节隔离开来。

​ 解耦合。

  • 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时,可使用该模式。

​ 减少了大量的条件判断if else

最后再看一遍他的概念:它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。 你是否有所感悟?