【文章标题】用面向对象的思想探讨游戏“魔兽争霸”(3)-模板模式的应用(修改版)
【文章作者】曾健生
【作者邮箱】zengjiansheng1@126.com
【作者QQ】190678908
【作者博客】http://blog.youkuaiyun.com/newjueqi
【编程环境】JDK 1.6.0_01
【作者声明】欢迎转载文章,但转载请保留文章的完整性以及注明文章的出处。
*******************************************************************************
本人在看完《大话设计模式》中的第10章——“考题抄错会做也白搭—模板方法模式”后就突然之灵光一闪,这个模板模式不就是改良在上篇文章《用面向对象的思想探讨游戏“魔兽争霸”(2)-继承和多态的应用》(详见本人的博客:http://blog.youkuaiyun.com/newjueqi)的 妙药。
我们比较一下在上篇文章《用面向对象的思想探讨游戏“魔兽争霸”(2)-继承和多态的应用》中弓箭手类Bower和食尸鬼类Ghost所覆盖战士类Fighter的方法public void getHunt( Fighter fighter )
其中弓箭手类Bower的getHunt方法如下:
//受到攻击时调用这个方法计算伤害值
public void getHunt( Fighter fighter )
{
//只有在食尸鬼和弓箭手都没死亡的前提下会攻击
if( fighter.getLifeNum()>0 && getLifeNum()>0 )
{
//如果受到的攻击值大于自身的生命值表示对象死亡
if( (fighter.getAttackNum()-getUntenNum())>=getLifeNum() )
{
setLifeNum( 0 );
System.out.print("弓箭手"+getId()+"受到食尸鬼"+fighter.getId());
System.out.print("的攻击力"+fighter.getAttackNum());
System.out.println(",弓箭手"+getId()+"死亡");
}
else //用生命值减去受到的伤害值
{
setLifeNum( getLifeNum()-(fighter.getAttackNum()-getUntenNum()) );
System.out.print("弓箭手"+getId()+"受到食尸鬼"+fighter.getId());
System.out.print("的攻击力"+fighter.getAttackNum());
System.out.println(",剩余生命值为"+getLifeNum());
}
}
}
食尸鬼类Ghost getHunt方法如下:
//受到攻击时调用这个方法计算伤害值
public void getHunt( Fighter fighter )
{
//只有在食尸鬼和弓箭手都没死亡的前提下会攻击
if( fighter.getLifeNum()>0 && getLifeNum()>0 )
{
//如果受到的攻击值大于自身的生命值表示对象死亡
if( ( fighter.getAttackNum()-getUntenNum())>=getLifeNum() )
{
setLifeNum( 0 );
System.out.print("食尸鬼"+getId()+"受到弓箭手"+fighter.getId());
System.out.print("的攻击力"+fighter.getAttackNum());
System.out.println(",食尸鬼"+getId()+"死亡");
}
else //用生命值减去受到的伤害值
{
setLifeNum( getLifeNum() -
(fighter.getAttackNum()-getUntenNum()) );
System.out.print("食尸鬼"+getId()+"受到弓箭手"+fighter.getId());
System.out.print("的攻击力"+fighter.getAttackNum());
System.out.println(",剩余生命值为"+getLifeNum() );
}
}
}
大家仔细比较这两个方法的不同点,就除了所有的输出语句中的“食尸鬼”和"弓箭手"这两个字符串不同外(即各自类型的字符表达形式),其他的内容都是相同的。
在《《设计模式:可复用面向对象软件的基础》》中是这样定义模板模式:定义一个操作中的算法的骨架,而将一些步?延迟道子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定的步?。
分析getHunt 方法中的不变量与变化量:不变量是整个算法的步?,变化量是一个字符串的表示,我们根据模板模式的思想:把不变量写在父类中,在父类中把变化量抽象出来,而变化量的实现则延迟到子类中。
同理,可进一步抽象moveTo。
重构后的父类Fighter如下:
//战士类,所有的士兵都继承于这个类
abstract class Fighter
{
private int posX; //战士在地图上X的坐标
private int posY; //战士在地图上Y的坐标
private int id; //战士在地图上Y的坐标
private int lifeNum; //战士的生命值
private int attackNum; //战士的攻击力
private int untenNum; //战士的防御力
private String fightername; //战士的名称
public Fighter( int posX,
int posY,
int id,
int lifeNum,
int attackNum,
int untenNum,
String fightername ) {
this.posX = posX;
this.posY = posY;
this.id = id;
this.lifeNum = lifeNum;
this.attackNum = attackNum;
this.untenNum = untenNum;
this.fightername= fightername;
System.out.println( fightername+id+"生产完毕了");
}
/******
一般来说,生命值,攻击力,防御力等都属于对象的核心数据,对它们
的访问必须要严格控制,所以设计出getLifeNum(),getAttackNum(),
getUntenNum()这三个方法
*/
//获取战士的剩余生命值
public int getLifeNum()
{
return lifeNum;
}
//设置战士的生命值
public void setLifeNum( int num )
{
lifeNum=num;
}
//获取战士的攻击力
public int getAttackNum()
{
return attackNum;
}
//获取战士的防御力
public int getUntenNum()
{
return untenNum;
}
//获取战士的ID号
public int getId()
{
return id;
}
//获取战士的X坐标
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
//获取战士的Y坐标
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
//用战士攻击别人的方法,传入的参数为攻击的对象
//附:本人感觉这个攻击行为抽象的设计非常差,如果有好的方法,
//敬请指教
public void attack( Fighter fighter )
{
fighter.getHunt( this );
}
/*这里就体现了模板模式的思想了,把需要获得的字符串(如"弓箭手"和"食尸鬼"等的获
得用publicString getFightername( ),是返回字符串fightername的值,但是在父类中,这个字符串是空的,这个字符串的真正赋值是在子类的构造函数中的,所以就能把父类的实现延迟到子类中,达到了最大程度的代码复用。 */
public String getFightername( )
{
return fightername;
}
//战士的移动行为,就是一般情况下用点击了
//一个战士后命令战士移动到某个位置所用的方法
//传入参数为要移动到的对象
public void moveTo( Fighter fighter )
{
posX=fighter.getPosX();
posY=fighter.getPosY();
System.out.println( this.getFightername( )+getId()+"移动到地点 "
+posX+","+posY );
}
//受到攻击时调用这个方法计算伤害值
public void getHunt( Fighter fighter )
{
//只有在食尸鬼和弓箭手都没死亡的前提下会攻击
if( fighter.getLifeNum()>0 && getLifeNum()>0 )
{
//如果受到的攻击值大于自身的生命值表示对象死亡
if( (fighter.getAttackNum()-getUntenNum())>=lifeNum )
{
lifeNum=0;
System.out.print(this.getFightername( )+getId()+
"受到"+fighter.getFightername( )+fighter.getId());
System.out.print("的攻击力"+fighter.getAttackNum());
System.out.println(","+getFightername( )+getId()+"死亡");
}
else //用生命值减去受到的伤害值
{
lifeNum=lifeNum-(fighter.getAttackNum()-getUntenNum());
System.out.print( getFightername( )+getId()+"受到"+getFightername( )
+fighter.getId() );
System.out.print("的攻击力"+fighter.getAttackNum());
System.out.println(",剩余生命值为"+getLifeNum());
}
}
}
}
下面就是弓箭手类和食尸鬼类的实现
//这是一个弓箭手类
class Bower extends Fighter
{
//构造函数,生产一个弓箭手,传入参数为在地图中的坐标
public Bower( int posX,
int posY,
int id,
int lifeNum,
int attackNum,
int untenNum,
String fightername )
{
super( posX,
posY,
id,
lifeNum,
attackNum,
untenNum,
fightername );
}
}
//这是一个食尸鬼类
class Ghost extends Fighter
{
public Ghost( int posX,
int posY,
int id,
int lifeNum,
int attackNum,
int untenNum,
String fightername )
{
super( posX,
posY,
id,
lifeNum,
attackNum,
untenNum,
fightername );
}
}
大家和上篇文章《用面向对象的思想探讨游戏“魔兽争霸”(2)-继承和多态的应用》(详见本人的博客:http://blog.youkuaiyun.com/newjueqi)比较一下,可以发现代码已经精简了很多,而且修改代码时只需要修改父类就行,两个子类都不需要改动了^-^