先上最后的效果图:
牛战士使用了大刀,造成了25点伤害,史莱姆王还有125点生命
史莱姆王使用了打空气,没有命中对手,牛战士还有100点生命
牛战士使用了大刀,造成了25点伤害,史莱姆王还有100点生命
史莱姆王使用了打空气,没有命中对手,牛战士还有100点生命
牛战士使用了大刀,造成了25点伤害,史莱姆王还有75点生命
史莱姆王使用了打空气,没有命中对手,牛战士还有100点生命
牛战士使用了大刀,造成了25点伤害,史莱姆王还有50点生命
史莱姆王使用了打空气,没有命中对手,牛战士还有100点生命
牛战士使用了大刀,造成了25点伤害,史莱姆王还有25点生命
史莱姆王使用了打空气,没有命中对手,牛战士还有100点生命
牛战士使用了扎绷带,恢复了20点生命,牛战士还有120点生命
史莱姆王使用了身体攻击,造成了30点伤害,牛战士还有90点生命
牛战士使用了扎绷带,恢复了20点生命,牛战士还有110点生命
史莱姆王使用了身体攻击,造成了30点伤害,牛战士还有80点生命
牛战士使用了扎绷带,恢复了20点生命,牛战士还有100点生命
史莱姆王使用了身体攻击,造成了30点伤害,牛战士还有70点生命
牛战士使用了大刀,造成了25点伤害,史莱姆王还有0点生命
史莱姆王倒下了,牛战士获得了胜利!
Process finished with exit code 0
可以看到这个结果有点小时候在诺基亚手机上玩的那种文字游戏的味道了
策略模式概念
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
策略模式的使用
HeadFirst中策略模式的使用是基于对鸭子的改造;
其类图如下
在策略模式中主要实现了几个关键的设计原则:
- 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
- 针对接口编程,而不是针对实现编程
- 多用组合,少用继承
demo 实现
接下来我们用策略模式实现一个小的demo,实现游戏中人物战斗的功能。
在这个demo中,人物可以装备不同的武器,并且可以使用不同的技能,怪物也有和人物一样的行为,每种怪物的战斗方式都不一样。
类图
设计思路
由类图可见Hero类作为顶级父类,其中组合了技能接口Skill和武器接口Weapon,因此每个英雄都可以自由的替换技能和武器。
在实现类中,我暂时实现了两个英雄类,一个Soldier类,是战士,攻击时有概率使用技能。另一个是Slime类,即史莱姆,作为一个弱鸡怪物,每次攻击都有50%概率被闪避。技能方面暂时实现了一个Bandager,扎绷带,回血;一个miss,即没有打中。武器方面,实现了一个Body类,身体攻击,没有任何增益;另一个Broadsword类,使用大刀,增加攻击伤害。
Skill算法族
Skill接口 参数为两个英雄对象,以此实现技能的释放,返回值为String类型 方便进行战斗的文字播报
public interface Skill {
public String skillDo(Hero att,Hero def);
}
Miss类 作为弱鸡史莱姆的技能
public class Miss implements Skill{
private String name ="打空气";
@Override
public String skillDo(Hero att, Hero def) {
return att.getName()+"使用了"+getName()+",没有命中对手,"+def.getName()+"还有"+def.getHp()+"点生命";
}
public String getName() {
return name;
}
}
Bandager类 作为战士的技能
public class Bandager implements Skill {
private int addHp;
private String name="扎绷带";
@Override
public String skillDo(Hero att, Hero def) {
int recover = this.getAddHp();
att.setHp(att.getHp()+this.getAddHp());
return att.getName()+"使用了"+getName()+",恢复了"+recover+"点生命,"+att.getName()+"还有"+att.getHp()+"点生命";
}
public String getName() {
return name;
}
public Bandager(int addHp) {
this.addHp = addHp;
}
public int getAddHp() {
return addHp;
}
}
Weapon算法族
Weapon接口 这里参数其实没有必要传入两个英雄,不过为了统一战斗的文字播报就和技能接口设置了一样的参数。
public interface Weapon {
public String perform(Hero att,Hero def);
}
Body类 史莱姆使用身体进行撞击
public class Body implements Weapon {
private String name = "身体攻击";
/*
使用身体攻击,造成atk点伤害
*/
@Override
public String perform(Hero att, Hero def) {
int damage = att.getAtk();
def.setHp(def.getHp()-damage>0?def.getHp()-damage:0);
return att.getName()+"使用了"+getName()+",造成了"+damage+"点伤害,"+def.getName()+"还有"+def.getHp()+"点生命";
}
public String getName() {
return name;
}
}
Broadsword类 战士抗大刀
public class Broadsword implements Weapon {
private String name="大刀";
private int atk = 10;
@Override
public String perform(Hero att, Hero def) {
int damage = att.getAtk()+this.getAtk();
def.setHp(def.getHp()-damage>0?def.getHp()-damage:0);
return att.getName()+"使用了"+getName()+",造成了"+damage+"点伤害,"+def.getName()+"还有"+def.getHp()+"点生命";
}
public void setAtk(int atk) {
this.atk = atk;
}
public String getName() {
return name;
}
public int getAtk() {
return atk;
}
}
Hero类与其子类
public abstract class Hero {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Weapon weapon;
private Skill skill;
private int hp;
private int atk;
public abstract String fight(Hero def);
private String useSkill(Hero def){
return this.skill.skillDo(this,def);
}
private String useWeapon(Hero def){
return this.weapon.perform(this,def);
}
public Hero() {
}
public Hero(int hp, int atk) {
this.hp = hp;
this.atk = atk;
}
public Hero(int hp, int atk,Weapon weapon, Skill skill) {
this.weapon = weapon;
this.skill = skill;
this.hp = hp;
this.atk = atk;
}
public Weapon getWeapon() {
return weapon;
}
public Skill getSkill() {
return skill;
}
public int getHp() {
return hp;
}
public int getAtk() {
return atk;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public void setSkill(Skill skill) {
this.skill = skill;
}
public void setHp(int hp) {
this.hp = hp;
}
public void setAtk(int atk) {
this.atk = atk;
}
}
Soldier类 战士
public class Soldier extends Hero {
public Soldier() {
}
public Soldier(int hp, int atk) {
super(hp, atk);
}
public Soldier(int hp, int atk, Weapon weapon, Skill skill) {
super(hp, atk, weapon, skill);
}
/*
战士战斗中有30%的概率使用主动技能,表现为当随机数<30时使用技能,否则使用武器攻击。
*/
@Override
public String fight(Hero def) {
Random random = new Random();
int result=random.nextInt(100);
if (result<30){
return this.getSkill().skillDo(this,def);
}else {
return this.getWeapon().perform(this,def);
}
}
}
Slime类 史莱姆
public class Slime extends Hero {
/*
史莱姆攻击有50%概率被闪避 所以当随机数小于50时,史莱姆使用技能"攻击被闪避",否则进行攻击
*/
public Slime() {
}
public Slime(int hp, int atk) {
super(hp, atk);
}
@Override
public String fight(Hero def) {
Random random = new Random();
int result=random.nextInt(100);
if (result<50){
return this.getSkill().skillDo(this,def);
}else {
return this.getWeapon().perform(this,def);
}
}
}
角斗场测试
public class Arena {
public static void main(String[] args) {
Hero hero1 = new Soldier(100,15,new Broadsword(),new Bandager(20));
hero1.setName("牛战士");
Hero hero2 = new Slime(150,30);
hero2.setWeapon(new Body());
hero2.setSkill(new Miss());
hero2.setName("史莱姆王");
System.out.println(pk(hero1,hero2));;
}
public static String pk(Hero hero1,Hero hero2){
String result=hero1.getName()+"倒下了,"+hero2.getName()+"获得了胜利!";
while (hero1.getHp()>0&&hero2.getHp()>0){
String info;
if (hero1.getHp()>0){
info= hero1.fight(hero2);
System.out.println(info);
}else{
result=hero1.getName()+"倒下了,"+hero2.getName()+"获得了胜利!";
}
if (hero2.getHp()>0){
info=hero2.fight(hero1);
System.out.println(info);
}else {
result=hero2.getName()+"倒下了,"+hero1.getName()+"获得了胜利!";
}
}
return result;
}
}
测试了几次 在这个数据下 战士和史莱姆王旗鼓相当,为了让战士能更好的打败史莱姆王,大家还是要多多氪金啊!
最后关于文字播报还有许多可以优化的地方,比如可以用写一个方法来进行战斗播报等,不过限于晚自习要下课了,我就不写啦