请教一个observer设计问题。

刚刚学了一点事件处理,准备做个编程练习。
可是一到实践,就出现了很多问题。具体是这样的:

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);
//...
}

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

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

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

1.坦克永远也不会感知导弹来了,进而更不可能知道自己被导弹炸掉了。坦克只能被炸掉能感受爆炸的方向,不可能知道是为什么爆炸的。
2.导弹不会知道每个坦克的位置,他只能检测自己周围一定的范围。

我们来了解一下真实环境中的情况:
导弹:自动发现并锁定目标,然后以目标为方向随时修正自己的航向。引爆有2种方式:撞击引爆自己,到达一定距离后“近距”引爆自己。
环境:感受到导弹爆炸,将爆炸的效果向4周传播。
坦克:感受到爆炸的效果和方向,然后根据自己的物理结构受到爆炸的物理损伤。

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

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

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

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

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

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

//////////
还有就是TankEvent和MissileEvent要怎么设计呢?
没用的属性该怎样处理?这里能不能处理的干净一些?


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

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

坦克大战写到后来,我发现事件消息最多只有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修改过]

一般的观察者模式可以看看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);

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

谢谢楼上的代码,受益匪浅。

现在又出现一个新的问题,就是如果我为每一辆坦克、每一发炮弹、每一个事件通知和绘图 都单开线程的话,会不会造成这样的结果:

一辆坦克线程已经移动了n次,而绘图线程只运行了1次。图像出现闪烁,坦克跳动前进?