请教一个observer设计问题。

08-12-21 darasion
              

刚刚学了一点事件处理,准备做个编程练习。

可是一到实践,就出现了很多问题。具体是这样的:

1、一个坦克大战游戏;

2、战场上有M枚飞行中的导弹;

3、战场上有N辆行进中的坦克;

如何处理下面两种事件?

事件1:坦克被导弹击中

事件2:导弹击中了坦克

也就是说:一枚导弹击中了坦克的同时,就有一辆坦克被这枚导弹击中。

我的问题是,如何检测这两个事件的发生比较好呢?

1、坦克获得每个导弹的位置,并检测是否相撞,如果相撞则调用this.notifyTankAttacked()和hisMissile.notifyMissileHitTank();

2、导弹获得每个坦克的位置,并检测是否相撞,如果相撞则调用this.notifyMissileHitTank()和hisTank.notifyTankAttacked();

3、由其他线程获得坦克和导弹的位置,循环每个坦克,再拿每个坦克的位置跟每个导弹的位置相比较,循环嵌套;当确认相撞后,调用tank.notifyTankAttacked()和

missile.notifyMissileHitTank();

4、有其他更好的办法。。。

//////////////////////以下是部分代码/////////////////////

这里有些不知所措,

我该如何定义事件?

这里仅考虑了tankAttacked()这个事件,它有两个参与者

1、发生该事件的坦克

2、击中坦克的导弹

如果坦克还有其他事件,如:tankFire()坦克开炮/tankMove()坦克移动/tankDie()坦克死掉/...

那么在这些事件中,第二个参数attackerMissile(击中坦克的导弹)将没有任何用处了。

同样的问题也在MissileEvent中存在。

不知道这里应该如何处理?

我在想,这个TankEvent事件可不可以看作是DTO?也就是粗粒度的,我可以把所有可能用到的属性都加上,不用的属性就传递成null即可了?

public class TankEvent{
	private Tank tank;
	private Missile attackerMissile;
	//getter/setter
	//...
}

public class MissileEvent{
	private Missile missile;
	private Tank tankAttacked;
	////getter/setter
	//...
}

//坦克
public class Tank{
	//...
	private List<TankEventListener> listeners;
	public void notifyTankAttacked(){
		for(Iterator<TankEventListener> i = listeners.iterator();i.hasNext();){
			i.next().tankAttacked(new TankEvent(...));
		}
	}
}

//导弹
public class Missile{
	//...
	private List<MissileEventListener> listeners;
	public void notifyMissileHitTank{
		for(Iterator<MissileEventListener> i = listeners.iterator();i.hasNext();){
			i.next().missileHitTank(new MissileEvent(...));
		}
	}
}


public interface TankEventListener{
	public void tankAttacked(TankEvent e);
	//...
}

public interface MissileEventListener{
	public void missileHitTank(MissileEvent e);
	//...
}
<p>

[该贴被darasion于2008-12-21 19:03修改过]

[该贴被darasion于2008-12-21 20:11修改过]

              

IceQi
2008-12-21 21:56

你的消息处理基本对了,只是需要考虑使用线程进行消息的通知。

问题在你的系统设计。我们遇到的软件基本上是在模拟现实的世界,所以当我们进行软件设计的时候首先需要思考一下真实世界的样子。

1.坦克永远也不会感知导弹来了,进而更不可能知道自己被导弹炸掉了。坦克只能被炸掉能感受爆炸的方向,不可能知道是为什么爆炸的。

2.导弹不会知道每个坦克的位置,他只能检测自己周围一定的范围。

我们来了解一下真实环境中的情况:

导弹:自动发现并锁定目标,然后以目标为方向随时修正自己的航向。引爆有2种方式:撞击引爆自己,到达一定距离后“近距”引爆自己。

环境:感受到导弹爆炸,将爆炸的效果向4周传播。

坦克:感受到爆炸的效果和方向,然后根据自己的物理结构受到爆炸的物理损伤。

用这样的方式重新思考一下。

darasion
2008-12-21 22:26

哎呀,这样就太复杂了。我只想做个练习,不是做真的游戏。

我这个missile不是真正的导弹,我这个没有制导的,更确切地说,是炮弹。

炮弹的特点就是:他不知道目标是谁,只按照一条线往前飞。

这样的话问题就来了,我是用炮弹来撞坦克呢,还是用坦克来撞炮弹?还是另外做个“裁判”?

您说的用线程作消息通知,这个我有点不太理解,能不能详细的解释一下?

//////////

还有就是TankEvent和MissileEvent要怎么设计呢?

没用的属性该怎样处理?这里能不能处理的干净一些?

[该贴被darasion于2008-12-21 22:30修改过]

[该贴被darasion于2008-12-21 22:31修改过]

darasion
2008-12-24 16:21

编程练习还是很有用处的 :)

坦克大战写到后来,我发现事件消息最多只有2个参数:一个是发起事件的主动者,另一个是接受事件的被动者。

例如:

1、包含主动与被动的事件:

导弹击中坦克、导弹击中墙壁、坦克撞了另一辆坦克、坦克撞了墙壁、坦克被导弹击中、墙壁被导弹击中等。

2、只有被动的事件(主动者是游戏本身):

坦克被创建、坦克被清除、导弹被创建、导弹被清除、爆炸被创建、爆炸被清除。

因此,我在游戏中删除了TankEvent/MissileEvent/ExplodeEvent,取而代之的是直接将 [事件的主动发起者]和[事件的被动接受者]传递给相应的事件处理方法。

至于事件通知由谁调用,我在“坦克大战”的实践中暂时觉得,由发起事件的主动一方调用比较合适,因为只有主动一方最知道事情的原委,正如2楼所说。

----------------------

此外,之前的代码中存在线程同步问题,事件通知方法notify...()和addEventListener()要加synchornized。

问题在测试中已经得到证实。

----------------------

[该贴被darasion于2008-12-24 16:23修改过]

[该贴被darasion于2008-12-24 16:27修改过]

[该贴被darasion于2008-12-24 16:32修改过]

[该贴被darasion于2008-12-24 16:43修改过]

IceQi
2008-12-24 22:41

一般的观察者模式可以看看JDK中的Observable/Observer部分描述。你的结构中缺少了最重要的一点:被通知的消息。

线程的方式大概如下:

class Notifier implements Runnable

{

Observer dest;

Observable src;

Object msg;

public void run()

{

dest.update(src, msg);

}

}

使用方法:

Notifier n = new Notifier();

n. dest = dest;

n.src = src;

n.msg = msg;

ThreadPool.execute(n);

使用线程提高并行处理能力。

2Go 1 2 下一页