雍禾植发帝之java设计模式学习(一)策略模式

雍禾植发帝之java设计模式学习(一)策略模式

策略模式

1. 策略模式的基本内容

  • 定义:(Strategy Pattern)定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
  • 遇到什么问题时想要使用策略模式:有多个不同的类,它们不同的地方仅是它们的行为方法,或者是某些类中,它们的某些行为方法需要经常的改变,当面向实现编程时,我们的代码被实现绑死,所以当改变来临时(比如你的老板让你修改需求时),我们的工作量是非常巨大的(请参考后面的例子)
  • 如何解决:将这些经常需要改变的行为方法从类中抽离出来,另外形成一组类(暂时将其称为’锦囊’),当原本的类需要某个行为方法时,则从锦囊中取出对应的行为方法给它,当需要更换行为方法时,则只需要从锦囊中取出另外一个行为方法替换就可以达到目的(这样既达到复用的目的,也不让原本的类被实现绑死)

2. 策略模式的“由来”

2.1“继承”局限性

场景一:
当小明设计一款冒险游戏时,有三个不同的角色(King、Queen、Knight),每个角色有一个display()会告诉玩家他们是什么角色,小明理所当然的想到Java基础中的继承来实现:
雍禾植发帝之java设计模式学习(一)策略模式

场景二:
第二天,游戏策划告诉小明,角色应该可以战斗,小明想这还不简单,只需要在Character抽象类中添加一个fight(),然后在每一个角色实现类中实现这个方法,那不就行了吗,说干就干,小明已经想到自己完成之后下班要去吃什么了:(注意:图中fight()在不同的角色实现中,可能是用不同的武器去战斗,在这里就不画出来了)
雍禾植发帝之java设计模式学习(一)策略模式

场景三:
然而too young too simple,就在小明提交了自己的想法没多久,策划就打来了电话:小明,Queen王后是不会战斗的,怎么你的王后跟着国王下战场了?!
小明:啊,还有这种情况,不过难不倒我,这很简单,只要在Queen的实现类中,让fight()什么都不做,这样王后就不会去战斗了:
雍禾植发帝之java设计模式学习(一)策略模式

场景四:
到了这个时候,小明突然想到,如果过两天策划突然决定添加一个生育系统giveBirth(),到时候若是在抽象类Character类添加该功能,那岂不是所有的角色(包括KingKnight)都会生小孩了,到时候要避免这种情况,岂不是又要在KingKnight的实现类中,覆盖掉giveBirth(),让它们什么都不做(类似于场景三),想到那样的工作量,小明就隐隐一阵头疼

使用继承来提供Character的行为,会导致以下的缺点

  • 代码在多个子类中重复
  • 运行时的行为不容易改变
  • 很难知道所有角色的全部行为
  • 改变会牵一发而动全身,造成其他角色不想要的改变

那要是接口的话呢?:

场景五:
小明将fight()提取成一个FightBehavior接口,当以后新增加的角色会战斗时,则实现该接口,若不会战斗时,就不实现该接口,如下图中的王后,这样总算可以解决刚刚的问题了吧。
雍禾植发帝之java设计模式学习(一)策略模式

 

其实这样又出现了新的问题:虽然解决了一部分的问题,却造成代码无法“复用”的问题(当不同的角色使用相同的方式战斗时,fight()需要在不同的实现类中实现多次。而且当这个方法发生改变时,修改起来的代码量是很多),有没有什么办法,既可以提高代码“复用”率,并且当需求改变时,可以用最小的代码量修改就达成目标呢?

2.2 “策略模式”登场

将经常需要改变的行为方法从类中抽离出来,另外形成一组类(暂时将其称为’锦囊’),当原本的类需要某个行为方法时,则从锦囊中取出对应的行为方法给它,当需要更换行为方法时,则只需要从锦囊中取出另外一个行为方法替换就可以达到目的(这样既达到复用的目的,也不让原本的类被实现绑死)

这句话的意思有点抽象,让我们代入到小明的开发场景中去:
既然fight()战斗这个行为方法常常需要改变,干脆把它从具体的类实现中抽离出来,将各种武器(Knife、Sword、Arrow,同时也有’空武器’)放入一个战斗锦囊,然后给每一个角色发放一个战斗锦囊,当角色需要某一个武器时,则从锦囊中取出相应的武器就好了
雍禾植发帝之java设计模式学习(一)策略模式

  • 只有Character类实现了fight()方法,这个方法让角色使用武器锦囊
  • 此处每一个角色都继承了来自父类的WeaponBehavior wb;,并且在构造函数处将对应的武器实现类newwb

雍禾植发帝之java设计模式学习(一)策略模式

2.3 “策略模式”中的设计原则

  1. 而在“策略模式”中,我们强调将经常发生改变的行为方法从类中抽离出来,这一步就是体现了设计原则中的"封装变化"
  2. 在具体的角色类中,角色只需要知道调用行为方法的接口,而不需要管该方法的具体实现,而这体现的就是设计原则中的"接口编程"
  3. 将原本的类和封装变化而产生的类二者相结合起来使用,这就是“组合”,体现了设计原则中的"多用组合,少用继承

策略模式的介绍就到这里!

####此处粘上来代码(其中角色类和武器类分别只贴上来一个)

//Character类(抽象类)
public abstract class Character {
	
    WeaponBehavior wb; 

    public abstract void display();

    public void fight() {
        wb.userWeapon();
    }
}

//King类(继承自Character类)
public class King extends Character {
    public King() {
        wb = new SwordBehavior();   //在此处修改角色对应的战斗方式:此处让他从武器锦囊中取出 “剑”
    }

    @Override
    public void display() {
        System.out.println("我是国王");	
    }
	
}

//武器锦囊接口
public interface WeaponBehavior {
    public void userWeapon();
}

//武器:剑(实现了接口 'WeaponBehavior')
public class SwordBehavior implements WeaponBehavior{

    @Override
    public void userWeapon() {
        System.out.println("使用 剑 攻击!");
    }
}
	
//测试类
public class CharacterGame {
    public static void main(String args[]) {
        Character king = new King();
        king.display();
        king.fight();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

一个练习用的例子:人、鸡和鸭、马(吃饭和睡觉的方法都不一样,应用’策略模式’应该如何设计)

上一篇:LIME算法:图像分类解释器(代码实现)


下一篇:JavaScript012,判断语句