[设计模式学习笔记] -- 策略模式

策略模式算法

定义了算法族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。编程

  举一个简单的例子来描述策略模式。ide

  设计一款冷兵器时代士兵打仗的游戏,游戏内部设计要使用OO技术。函数

  首先,设计一个士兵对象(Soilder)做为父类,而后在设计许多不一样种类的士兵对象来继承士兵这个父类,好比:长枪兵(Spearman)、骑兵(Cavalryman)、弓箭手(Bowman)等等,设计好后进行讨论,以为不错没有问题,能够开始开发,测试,游戏公测……一切都不错,具体以下面的类图所示:测试

  

  在游戏运营了一段时候以后,发现须要一些新元素来吸引玩家,要求不一样种类的士兵的攻击动做不一样,而且要求士兵须要有游泳、攀爬等技能。使用OO技术很是好解决啊,打开类的设计图开始干活吧!能够把hit方法设计成抽象方法,让每种士兵都实现本身的hit,而后在Soilder类中加入游泳,攀爬的方法,修改的类图以下:this

  

  使用这种设计方式后,在游戏运行的过程当中咱们会看到,骑兵骑着马在爬树。显然的,在设计的时候忽略了一点,并不是全部的士兵均可以游泳和攀爬。有许多不可作这些行为的士兵,对代码的局部修改,影响的层面可不只是局部。spa

  使用继承如何?.net

  把swim和clmb放到子类中,覆盖掉父类的对应方法,像hit的作法同样。可若是之后要加入其余类型的士兵又会如何?好比重甲步兵没办法游泳也没办法攀爬,由于重甲过重了,而骑兵则没法骑着马攀爬,但能够骑着马过河等等。可见利用继承来提供士兵的行为会形成:设计

  1.代码在多个子类中重复;3d

  2.运行时的行为不容易改变;

  3.很难知道全部士兵的所有行为;

  4.改变会牵一发而动全身,形成其余某些类型的士兵不想要的行为;

  利用接口如何?

  能够加入ISwin和IClimb接口,把swim函数和climb函数从父类中取出来,并被子类实现,以下图所示:

  

  但士兵的规格会经常改变,每当有新的士兵类型出现,就要被迫检查兵可能须要实现ISwim和IClimb接口。而且这么以来重复的代码会变多,由于实现的能够游泳和爬树的代码是同样的。好比有一天swim的方式发生了变化,现有的50个士兵子类都须要修改swim函数,这是不能够接受的。

  如何解决?

  能够看出,并不是全部的子类都会游泳和爬树,因此继承并非适当的解决方式。虽然使用接口能够解决一部分问题,可是却形成了代码没法复用,幸运的是,有一个原则刚好用于此处。

设计原则1

找出应用中可能须要变化之处,把它们独立出来,不要和那些不须要变化的代码混在一块儿。

  也就是说,每次需求一来,都会使某些方面的代码发生变化,那么基本能够肯定这部分代码须要被抽出来和其余稳定的代码有所区分。

  如今,为了要分开变化和不会变化的部分,咱们准备创建两组类,一个是swim相关的,一个是clmb相关的,而且要作到一切能有弹性,正由于一开始设计的行为没有弹性才致使种种问题。比方说,我要产生一个新的类型的轻甲士兵,并指定行为攀爬给他,也就是说在士兵类中包涵设置行为的方法,这样就能够在运行时动态改变行为,有了这些目标要实现,就引出了第二个设计原则。

设计原则2

针对接口或抽象类编程,而不是针对实现编程。

  咱们利用接口表明每一个行为(Behavior),比方说SwimBehavior和ClimbBehavior,而行为的每一个实现都将实现其中的一个接口。Soilder类不会负责实现它们,反而是由一组其余类专门去实现,并整合到Soilder类中,具体的作法是在Soilder类中加入两个实例变量,分别为SwimBehavior和ClimbBehavior,声明为接口类型,每一个士兵子类都会动态的设置这些变量在运行时引用正确的行为类型。看一下类图与实现的代码(Java代码)。

  

  

package cn.net.bysoft.Strategy;

//    士兵
public abstract class Soldier {
    //    士兵的名字
    private String name;
    private SwimBehavior swimBehavior;
    private ClimbBehavior climbBehavior;

    public Soldier() {
        super();
    }

    public Soldier(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public SwimBehavior getSwimBehavior() {
        return swimBehavior;
    }

    public void setSwimBehavior(SwimBehavior swimBehavior) {
        this.swimBehavior = swimBehavior;
    }

    public ClimbBehavior getClimbBehavior() {
        return climbBehavior;
    }

    public void setClimbBehavior(ClimbBehavior climbBehavior) {
        this.climbBehavior = climbBehavior;
    }

    //    显示士兵的信息
    public void display() {
        System.out.println("这个士兵的名字:" + name);
    }
    
    //    士兵攻击敌人。
    public abstract void hit();
    
    //    委托给行为去处理。
    
    public void swim() {
        swimBehavior.swim();
    }
    
    public void climb() {
        climbBehavior.climb();
    }
}
士兵类的代码
package cn.net.bysoft.Strategy;

//    长枪兵
public class Spearman extends Soldier {
    public Spearman() {}
    
    public Spearman(String name) {
        super(name);
    }

    @Override
    public void hit() {
        // TODO Auto-generated method stub
        System.out.println("士兵使用长枪去攻击敌人");
    }
}


package cn.net.bysoft.Strategy;

//    骑兵
public class Cavalryman extends Soldier {
    
    public Cavalryman() {}
    
    public Cavalryman(String name) {
        super(name);
    }

    @Override
    public void hit() {
        // TODO Auto-generated method stub
        System.out.println("骑士在立刻用刀攻击敌人");
    }
}


package cn.net.bysoft.Strategy;

//    弓箭手
public class Bowman extends Soldier {
    
    public Bowman() {}
    
    public Bowman(String name) {
        super(name);
    }

    @Override
    public void hit() {
        // TODO Auto-generated method stub
        System.out.println("弓箭手拉弓瞄准射击敌人");
    }
}
长枪兵、骑士、弓箭手等士兵的代码
package cn.net.bysoft.Strategy;

//    游泳行为的接口。
public interface SwimBehavior {
    //    只需在此定义一个游泳的行为方法便可。
    public void swim();
}


package cn.net.bysoft.Strategy;

//    不能游泳
public class SwimNoWay implements SwimBehavior {

    public void swim() {
        // TODO Auto-generated method stub
        System.out.println("这个士兵不能游泳");
    }
    
}


package cn.net.bysoft.Strategy;

//    士兵本身游泳,不经过其余途径。
public class SwimWithBody implements SwimBehavior{

    public void swim() {
        // TODO Auto-generated method stub
        System.out.println("士兵本身游泳过河");
    }
    
}


package cn.net.bysoft.Strategy;

//    士兵骑马游泳
public class SwimWithHouse implements SwimBehavior {

    public void swim() {
        // TODO Auto-generated method stub
        System.out.println("士兵骑马过河");
    }

}
游泳行为接口与其实现类的代码
package cn.net.bysoft.Strategy;

//    攀爬行为的接口
public interface ClimbBehavior {
    public void climb();
}


package cn.net.bysoft.Strategy;

public class ClimbNoWay implements ClimbBehavior {

    public void climb() {
        // TODO Auto-generated method stub
        System.out.println("这个士兵不能攀爬");
    }

}


package cn.net.bysoft.Strategy;

//    士兵爬树。
public class ClimbTree implements ClimbBehavior {

    public void climb() {
        // TODO Auto-generated method stub
        System.out.println("士兵开始爬树");
    }

}
攀爬行为接口与其实现类的代码
package cn.net.bysoft.Strategy;

public class Game {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        //    一个玩家创建一个长矛兵进入游戏
        Soldier spearman = new Spearman("长矛高手");
        spearman.hit();
        //    设置这个士兵的行为,能够本身游泳,爬树。
        spearman.setSwimBehavior(new SwimWithBody());    
        spearman.setClimbBehavior(new ClimbTree());
        spearman.swim();
        spearman.climb();
        
        System.out.println(" ============================= ");
        
        //    另外一个玩家创建士兵进入游戏
        Soldier cavalryman = new Cavalryman("圆桌骑士");
        cavalryman.hit();
        //    设置这个士兵的行为,能够骑马过河,可是不能够爬树。
        cavalryman.setSwimBehavior(new SwimWithHouse());
        cavalryman.setClimbBehavior(new ClimbNoWay());
        cavalryman.swim();
        cavalryman.climb();
    }
}
测试类,测试策略模式

  总结一下,请特别注意类之间的关系,关系能够是IS-A(是一个)也能够是HAS-A(有一个)或IMPLEMENTS(实现)。HAS-A关系至关有趣,每个士兵都有SwimBehavior和ClimbBehavior的行为,并将这些动做交给它们去处理,当将两个类结合起来使用,就是这种组合。这种作法和继承不一样的地方在于,行为不是继承来的,而是和适当的行为对象组合来的。这是一个很重要的技巧,骑士是使用了第三个设计原则:

设计原则3

多用组合,少用继承。

  使用组合创建系统具备很大的弹性,不只能够将算法族封装成类,更能够在运行时动态地改变行为,只要组合的行为对象符合正确的接口标准便可。以上就是策略模式的介绍。

相关文章
相关标签/搜索