设计模式-策略模式

原创
2019/03/27 16:33
阅读数 52

一、策略模式定义

      策略模式就是定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。看着定义可能晦涩难懂,那就接着往下看下解释,再回来看定义就会清晰明了。

 

二、策略模式演化(即为什么要使用策略模式)

      假设我们要设计一款游戏叫做英雄同盟(有点山寨啊~) 每个英雄都有名称属性、说话方法和攻击方法,下面我们来实现下

//超类
public abstract class Hero {
  String name;
  public Hero(name) {
    this.name = name
  }
  public void speak(){
    System.out.println('My name is ' + profession);
  }
  public void attack(){
    System.out.println('I am attacking now!')
  }
}
//战士类
public class WarriorHero extends Hero {
  //战士的防御属性
  Long defense;
  public WarriorHero(name) {
    super(name);
  }
  public void setDefense(Long defense){
    this.defense = defense
  }
}
//刺客类
public class AssassinHero extends Hero {
  //刺客的敏捷属性
  Long agile;
  public AssassinHero(name) {
    super(name);
  }
  public void setAgile(Long agile) {
    this.agile = agile;
  }
}

      但是根据我多年玩游戏的经验来看这个游戏设计的有问题,问题在哪呢?问题就是刺客有刺客的攻击方式,展示有战士的攻击方式。既然这样我们就再改造一下将超类中的攻击方法设置成抽象方法,在自类中进行实现。

//超类
public abstract class Hero {
  String name;
  public Hero(name) {
    this.name = name
  }
  public void speak(){
    System.out.println("My name is " + profession);
  }
  public abstract void attack();
}
//战士类
public class WarriorHero extends Hero {
  //战士的防御属性
  Long defense;
  public WarriorHero(name) {
    super(name);
  }
  public void setDefense(Long defense){
    this.defense = defense
  }
  public void attact() {
    System.out.println("I am attact with a axe(斧子)")
  }
}
//刺客类
public class AssassinHero extends Hero {
  //刺客的敏捷属性
  Long agile;
  public AssassinHero(name) {
    super(name);
  }
  public void setAgile(Long agile) {
    this.agile = agile;
  }
  public void attact() {
    System.out.println("I am attact with a dagger(匕首)")
  }
}

嗯!~以我多年玩游戏的经验,这下正常了。但是以我多年码代码的经验来看又有问题

attact方法与具体类耦合严重,并且无法做到复用。那如果再来十个职业要用到斧子,那我是不是要重新写十遍相同的attact方法呢?

 

三、策略模式

      上面已经提出了问题,那么应该用什么方法解决该问题呢?没错就是使用策略模式

3.1 UML图

     上面的uml图中我们看到这里我们将不同的攻击方法封装成了一个算法族,并且在抽象基类中存放了攻击方法的接口实例,这样我们就可以根据不同的不同的Hero中存放的不同的attact方法使用不同的攻击手段。废话少说,直接上代码。

 

3.2 代码演示

//算法族
//接口
public interface AttactBehavior {
  public void attact();
}

//斧子攻击
public class AxeAttact implements AttactBehavior {
  public void attact() {
    System.out.println("我正在用斧子攻击!")
  }
}

//用宝剑攻击
public class SwordAttact extends AttactBehavior {
  public void attact() {
    System.out.println("我正在用宝剑攻击!")
  }
}

//用匕首攻击
public class DaggerAttact extends AttactBehavior {
  public void attact() {
    System.out.println("我正在用匕首攻击!")
  }
}

 好,我们已经定义了包含三种攻击手段的算法族,接下来就是要使用这些算法族。

//超类
public abstract class Hero {
  String name;
  AttactBehavior attactBehavior;
  public Hero(name) {
    this.name = name;
  }
  public void speak() {
    System.out.println("我是" + this.name);
  }
  public void attack() {
    this.attactBehavior.attact(); //委托给攻击类进行处理
  }
  public void setAttactBehavior(attactBehavior) {
    this.attactBehavior = attactBehavior;
  }
}
//战士类
public class WarriorHero extends Hero {
  //战士的防御属性
  Long defense;
  public WarriorHero(name) {
    super(name);
    this.attactBehavior = new AxeAttact(); 
  }
  public void setDefense(Long defense){
    this.defense = defense;
  }
}
//刺客类
public class AssassinHero extends Hero {
  //刺客的敏捷属性
  Long agile;
  public AssassinHero(name) {
    super(name);
    this.attactBehavior = new DaggerAttact(); 
  }
  public void setAgile(Long agile) {
    this.agile = agile;
  }
}
//剑客类
public class SwordHero extends Hero {
  //刺客的敏捷属性
  Long power;
  public AssassinHero(name) {
    super(name);
    this.attactBehavior = new SwordAttact(); 
  }
  public void setPower(Long agile) {
    this.power = power;
  }
}

 现在我们也有了调用攻击类的Hero类,接下来我们来对他进行下测试

public class TestAttact {
  public static void main(String []args) {
    Hero swordHero = new SwordHero("剑客");
    swordHero.speak();
    swordHero.attact();
    System.out.println("------");
    Hero assassinHero = new AssassinHero("刺客");
    assassinHero.speak();
    assassinHero.attact();
    System.out.println("------");
    Hero warriorHero = new WarriorHero("战士");
    warriorHero.speak();
    warriorHero.attact();
  }
}

//输出结果
我是剑客
我正在用宝剑攻击!
------
我是刺客
我正在用匕首攻击!
------
我是战士
我正在用斧子攻击!

      通过策略模式优化过之后攻击的方法被拆分成了算法族,与Hero类进行了解藕,代码也可以进行复用。但是作为一名优秀的战士怎么可以只会使用斧子呢?那该怎么办?

public class TestAttact {
  public static void main(String []args) {
    Hero warriorHero = new WarriorHero("战士");
    AttactBehavior swordAttact = new SwordAttact();
    warriorHero.setAttactBehavior(swordAttact); //给战士一把宝剑
    warriorHero.speak();
    warriorHero.attact();
    System.out.println("------")
    AttactBehavior daggerAttact = new SwordAttact();
    warriorHero.setAttactBehavior(daggerAttact); //给战士一把匕首
    warriorHero.speak();
    warriorHero.attact();

  }
}

//输出结果

我是战士
我正在用宝剑攻击!
------
我是战士
我正在用匕首攻击

现在只要给Hero的实现类中的攻击类实例(即attactBehavior)任何攻击实现,他都可以进行攻击,也就是说你给英雄任何武器,他都能进行攻击。完美!

四、javascript中的应用

      又到了一年一度发年终奖的时候,小金的公司年终奖是按照绩效考核来发放的,绩效考核为S的发4倍的月工资,考核为A的发3倍月工资,考核为B的发2倍月工资,现在要写个方法计算员工的年终奖。

var calculateBouns = function(salary,level) {
    if(level === 'S') {
        return salary * 4;
    }
    if(level === 'A') {
        return salary * 3;
    }
    if(level === 'B') {
        return salary * 2;
    }
};

// 调用如下:
console.log(calculateBouns(4000,'A')); // 16000

看起来好像没什么问题,那么问题来了,如果项目奖金也是按照绩效来进行发放怎么办?我们岂不是又要在写一遍if else,下面来用策略模式进行优化。

var obj = {
        "S": function(salary) {
            return salary * 4;
        },
        "A" : function(salary) {
            return salary * 3;
        },
        "B" : function(salary) {
            return salary * 2;
        } 
};
var calculateBouns =function(level,salary) {
    return obj[level](salary);
};
var calculateProjectBonus = function(level,salary) {
    return obj[level](salary);
};
//年终奖
console.log(calculateBouns('S',10000)); // 40000
//项目奖
console.log(calculateProjectBonus('S',10000)) // 4000

五、总结

     策略模式指的是定义一系列的算法,并且把它们封装起来,但是策略模式不仅仅只封装算法,我们还可以对用来封装一系列的业务规则,只要这些业务规则目标一致,我们就可以使用策略模式来封装它们

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部