简介
策略模式定义
它定义了一系列算法,将每个算法都封装起来,并且使它们之间可以替换。策略模式让算法的变化独立于使用算法的客户端,即让算法变化不会影响到使用算法的客户端。
这个定义很难解释,我的理解是客户端可能会在多个不同的位置调用甚至组合调用算法,为了实现算法的复用与替换,算法独立出来,提供一个接口供客户端使用,客户端在接口中调用不同的算法,从而使算法与客户端隔离。
具体我们通过案例实现来了解
案例
假如我们现在正在设计一个BOSS,这个BOSS主要有三个动作普攻,技能,闪避。这三个动作可能不是一个BOSS专属的,可能其他BOSS也会复用,并且这个BOSS的技能后续可能也会调整。这个时候我们就需要采取策略模式来设计。
BOSS超类
Data
//BOSS超类
public abstract class Boss {
protected String name;
//动作组
protected IAttack attack;
protected IDodge dodge;
protected ISkill skill;
public Boss setAttack(IAttack iAttack)
{
this.attack=iAttack;
return this;
}
public Boss setDodge(IDodge iDodge)
{
this.dodge=iDodge;
return this;
}
public Boss setSkill(ISkill iSkill)
{
this.skill=iSkill;
return this;
}
//需要注意,这里的释放逻辑其实大部分情况下是不一样的,方法调用也不一定一样,我只是偷懒而已
public void attack() {
attack.release();
}
public void dodge() {
dodge.release();
}
public void skill() {
skill.release();
}
}
BOSS实现类
public class MartialGod extends Boss{
public MartialGod(){
this.name="MartialGod";
}
}
技能接口
public interface ISkill {
void release();
}
具体实现
//技能,剑舞
public class JianWu implements ISkill{
@Override
public void release() {
System.out.println("释放剑舞");
}
}
攻击接口
public interface IAttack {
void release();
}
具体实现
//普攻,扫腿
public class SaoTui implements IAttack{
@Override
public void release() {
System.out.println("释放扫腿");
}
}
闪避接口
public interface IDodge {
void release();
}
具体实现
//闪避,后跳
public class HouTiao implements IDodge{
@Override
public void release() {
System.out.println("释放后跳");
}
}
测试
@Test
public void test()
{
Boss boss=new MartialGod();
boss.setAttack(new SaoTui()).setDodge(new HouTiao()).setSkill(new JianWu());
boss.attack();
boss.dodge();
boss.skill();
}
测试结果
释放扫腿
释放后跳
释放剑舞
进一步案例
这个BOSS看起来就不太行的样子,正好我最近也看了设计模式中的状态模式,我们来对BOSS做一些加强,我们将BOSS设计为有两个阶段,两条独立血量,第一阶段血量归零则回满血进入第二阶段。第一阶段和第二阶段使用不同技能组。使用状态模式将BOSS的两个阶段独立出来。具体我们来看代码,这里我只介绍一下增加和修改的代码。
首先我们分别添加一个普攻,一个闪避,一个技能用于BOSS二阶段释放
//普攻,顺劈斩
public class ShunPiZhan implements IAttack{
@Override
public void release() {
System.out.println("释放顺劈斩");
}
}
//闪避,闪现
public class ShanXian implements IDodge{
@Override
public void release() {
System.out.println("释放闪现");
}
}
//技能,万剑天来
public class WanJianTianLai implements ISkill{
@Override
public void release() {
System.out.println("释放万剑天来");
}
}
然后我们修改一下BOSS类,将对应方法放入状态类中实现,并且添加一些额外的成员变量
@Data
//BOSS超类
public abstract class Boss {
protected String name;
//Boss血量
protected int bloodVolume;
//Boss二阶段血量
protected int secondBloodVolume;
//动作组
protected IAttack attack;
protected IDodge dodge;
protected ISkill skill;
//目前BOSS所处阶段
protected Stage stage;
}
BOSS实现类
public class MartialGod extends Boss{
public MartialGod(int Volume,int SecondVolume){
this.name="MartialGod";
this.bloodVolume=Volume;
this.secondBloodVolume=SecondVolume;
stage=new FirstStage(this);
}
}
状态抽象类
public abstract class Stage {
protected Boss boss;
public abstract void attack();
public abstract void dodge();
public abstract void skill();
//boss被攻击掉血
public abstract void attacked(int attack);
}
状态实现类,实际上这里的构造函数不应该固定给技能组,不利于扩展,应该在Boss类中添加方法,然后具体实现类实现安装技能组对应函数,这里构造函数直接调用对应的技能组,这样有利于扩展,这里偷懒懒得改了
//BOSS一阶段
public class FirstStage extends Stage{
public FirstStage(Boss boss){
this.boss=boss;
this.boss.setAttack(new SaoTui());
this.boss.setDodge(new HouTiao());
this.boss.setSkill(new JianWu());
}
//实际上攻击等操作可能会复杂,这里我全部简化了
@Override
public void attack() {
boss.attack.release();
}
@Override
public void dodge() {
boss.dodge.release();
}
@Override
public void skill() {
boss.skill.release();
}
@Override
public void attacked(int attack) {
boss.bloodVolume-=attack;
if(boss.bloodVolume<0){
boss.bloodVolume=0;
}
System.out.println("BOSS一阶段被攻击,受到伤害:"+attack+",Boss剩余血量:"+boss.bloodVolume);
if(boss.bloodVolume==0){
System.out.println("BOSS血量归零,进入二阶段");
boss.stage=new SecondStage(boss);
}
}
}
//BOSS二阶段
public class SecondStage extends Stage{
public SecondStage(Boss boss){
this.boss=boss;
this.boss.setAttack(new ShunPiZhan());
this.boss.setDodge(new ShanXian());
this.boss.setSkill(new WanJianTianLai());
}
//实际上攻击等操作可能会复杂,这里我全部简化了
@Override
public void attack() {
boss.attack.release();
}
@Override
public void dodge() {
boss.dodge.release();
}
@Override
public void skill() {
boss.skill.release();
}
@Override
public void attacked(int attack) {
boss.secondBloodVolume-=attack;
if(boss.secondBloodVolume<0){
boss.secondBloodVolume=0;
}
System.out.println("BOSS二阶段被攻击,受到伤害:"+attack+",Boss剩余血量:"+boss.secondBloodVolume);
if(boss.secondBloodVolume==0){
System.out.println("BOSS二阶段血量归零,战斗胜利");
//后面应该有结算函数,这里不写啦
}
}
}
测试
@Test
public void test()
{
Boss boss=new MartialGod(100,200);
//BOSS释放招式
boss.getStage().attack();
boss.getStage().dodge();
boss.getStage().skill();
//攻击BOSS
boss.getStage().attacked(60);
boss.getStage().attacked(60);
//BOSS二阶段释放技能
boss.getStage().attack();
boss.getStage().dodge();
boss.getStage().skill();
//攻击BOSS
boss.getStage().attacked(60);
boss.getStage().attacked(60);
boss.getStage().attacked(60);
boss.getStage().attacked(60);
}
测试结果
释放扫腿
释放后跳
释放剑舞
BOSS一阶段被攻击,受到伤害:60,Boss剩余血量:40
BOSS一阶段被攻击,受到伤害:60,Boss剩余血量:0
BOSS血量归零,进入二阶段
释放顺劈斩
释放闪现
释放万剑天来
BOSS二阶段被攻击,受到伤害:60,Boss剩余血量:140
BOSS二阶段被攻击,受到伤害:60,Boss剩余血量:80
BOSS二阶段被攻击,受到伤害:60,Boss剩余血量:20
BOSS二阶段被攻击,受到伤害:60,Boss剩余血量:0
BOSS二阶段血量归零,战斗胜利
这样整个程序也就写完了,这只是一个突发奇想的简单例子,与实际开发还是有很大区别的,主要还是练习一下设计模式的使用。
总结
可以看出,策略模式的优势就在于将算法族和应用者隔离开,应用者可以很方便的切换算法。但是缺点也很明显,就是会导致类数量的爆炸,尤其是程序复杂时。像我这个简单的例子都已经有10多个类了。而且对应用者要求高,应用者必须要知道每个算法,不符合迪米特原则。