请教一下,该如何重构或设计才好??

我自己在做一个网络文字游戏的东西,其中遇到这样一个问题,该如何重构或设计??请教各位高手:

首先,客户端发送一个command,服务器端由cammand模式调用服务器端的player类的行为函数,比如:a.talkto(),在talkto()函数中查找到你要交谈的对象是b,那么调用b.talkedBy(this);,然后在talkedBy()中查找a和b之间可以通过交谈激活的任务(task),然后调用task.talk(a);在task.talk()中查找到当前用户要完成的事件(一个任务由多个事件组成),调用event.talk(a),在event.talk()中判断用户有没有达到完成的要求,然后返回true或false;

我觉得这样一个调一个很是不好,并且再添加一个giveto()行为又要添加一串,应该怎么重构或设计才好???

你可不可以贴一个sequence diagram或者是collaboration diagram出来呀?这样绕来绕去都不明白你想实现什么!呵呵

呵呵,类似一个mud游戏是吗?
这样:


interface Object{
void accept(ObjectVisitor v);
}
interface Player extends Object{......}
interface ObjectVisitor{
void visitPlayer(Player p);
void visitNPC(NPC npc);
......
}
interface Action{
void accept(ActionVisitor v);
}
interface ActionVisitor{
void visitTalk(Player p, Object target);
void visitGive(Player p, Object target);
......//all other actions
}
class GameEngine implements ActionVisitor, GameState{
public void visitTalk(Player p, Object target){
try{
final Task t = findTaskForTalk(p, target);
for(Event event: t.getEvents()){
if(!event.complete(this, p)){
failEvent(p, event);
}
}
succeedTask(p, t);
}
catch(NoTaskException e){}
}
public void visitGive(Player p, Object target){
......
}
...
}

主循环接受玩家发来的消息,解释成一个一个的Action,然后发给GameEngine对象。(很可能是通过一个单独的线程来做。你可能要考虑用线程池哦,至少要先预留一个接口)
GameEngine对象包含所有的游戏逻辑。每一个动作所引发的相关动作和游戏状态变化都被封装在里面。它可以通过一个player的id找到player的socket从而给客户发送相关的信息。
另外,GameEngine也负责储存游戏状态,比如通过一些check point来储存。
GameEngine也必须是线程安全的。

所有的玩家所能发出的动作都由一个Action来代表。GameEngine通过ActionVisitor来区分不同的action。
游戏中所有的实体,包括物品,npc,wizard, player,都是一个Object。(这个Object不是java.lang.Object)
每个Player对象包含这个player的id等信息。GameEngine可以通过这个id找到所有关于这个player的状态。
每个event都设有一定的完成条件。event通过判断GameState的状态和Player的状态来决定是否符合条件。


这样,应该就差不多了。give和talk不过是两种不同的action。理论上,系统里可以存在任意多的action。


这里面,麻烦的,应该就是各个event的规则编写。简单的就是硬编码。如果要搞得好一些,只怕就要实现一个script来做这个事情。

多谢ajoo,我就是做一个mud游戏,游戏中所有的东西,包括任务事件,npc,地图,所有一切元素都是通过数据库定义的,npc,goods, player,map,都有一个package,走题了。。。,我的系统中用户的action是由command模式解析,顺序图:

Command最好不要直接调用Player.talkTo(NPC).
我之所以推荐visitor,是因为在visitor里面,你的Action什么也不干,只是负责描述这个action。比如:“ajoo跟椅子说'对不起'”, 或者“ajoo高举烤鸡腿对着鸡屁股当头砸了下来”之类的。

至于如何解释执行这些action,应该交给一个单独的visitor模块。
这样,给出不同的visitor模块,你可以实现完全不同的逻辑。

而且,talk就只能talk to NPC吗?不能talk to Player?

其它的先不说了,不是很清楚你的系统的需求,所以也不好说。

你这个设计其实是用了职责链模式,这是很耗费性能的。

在MUD游戏这样实时系统中,最好使用真正Command模式,将你的职责链压缩,就成了真正Command模式,至于如何具体做,要了解整个系统架构才能找出实用具体的方法。

我倒不觉得职责链有什么效率问题。实际上,很多商业库,如果你跟踪进去,都是几十层的嵌套,如果设计时就对效率太过关心,只怕就什么也干不了了。

如何设计,还是从如何减小耦合,如何减少代码重复着手。实际上,只要接口设计好了,管它叫什么模式呢?

如何实现一个真正的Command模式,对于你这样和客户端交互的例子来说,MVC是你努力的方向,你将你的系统的Event Action以及View、后台Model处理关系理顺,一般Action处使用Command模式。

to ajoo:
商业库不代表它的性能质量好啊,职责链因为涉及一个请求多个环节处理,很显然,处理环节越多,性能越低啊。

关于mvc模式我想我是这样做的,客户端就是view,command就是control

关于vistor我需要想一下先,不过“ajoo跟椅子说'对不起'”这些语句是从数据库中读取的,我希望我的mud通过修改数据库就可以变成另外一个mud

我的command是这样实现的(稍作简化):
public interface Command
{
public boolean action ( String cmdParams) ;
}

/**
* 交谈命令
*/

public class TalkToCmd extends AbstractCmd
{
protected boolean action(String cmdParams)
{ //conn是action带的参数,我省去了
conn.getUserControler().talkTo(cmdParams);

return true;
}
}

public class CommandDispatch
{
private Map responseMap = new HashMap () ;

public void putCommand ( String cmdString , Command cmd )
{
responseMap.put ( cmdString.toUpperCase () , cmd ) ;
}

public boolean action ( String cmdLine)
{
//分解命令行,比如用户发来的是talkto aaa,那么就分成
//cmdString=talkto;cmdParams=aaa
return internalAction (cmdString , cmdParams ) ;
}

protected boolean internalAction (String cmdString ,String cmdParams )
{
boolean result ;
//find command to do
Command cmd = ( Command ) responseMap.get ( cmdString ) ;
if ( cmd != null )
{
result = cmd.action ( cmdParams ) ;
}
return result ;
}
}


public class JMudCommandDispatch
extends CommandDispatch
{
...
public static String CMD_TALKTO = "TALKTO" ;

//Login
public JMudCommandDispatch ()
{

//用户命令
...
putCommand ( CMD_TALKTO , new TalkToCmd () ) ;
putCommand ( CMD_GIVETO , new GiveToCmd () ) ;

}

public boolean action ( String cmdLine )
{
return super.action ( cmdLine ) ;
}
}

player不是只能和npc交谈,如果和玩家交谈就是私聊,npc和humanPlayer继承至player,实现talkto(),
板桥说的职责链压缩,不太明白怎么压缩

好像所有的mud都是使用心跳模式的吧?

如果你的客户端发送到服务器端是编号的,如1代表play 2代表stop等,
那么压缩成Command模式就方便多。

如果,你不能明确客户端是编号发出命令,那么也参考看看Web结构中,前台浏览器一般都是发送一种命令:get或post,虽然只是一种命令,但是可以携带各种参数,例如参数1代表play 2代表stop等,依然可以使用MVC模式。

你先需要确定MVC框架,然后才可真正使用command模式。

Struts是Web结构中MVC好的实现,建议参考。

我的意思是说,在java里面,对效率不能这么个计较方法。
其实,所以职责链效率低,不过是因为消息经过了若干次indirection。但是,OO里面,decorator, adaptor, bridge等等等等,几乎所有的pattern都是通过加入一个额外的indirection来获得灵活性的。

从java的角度来看,只要算法合适,设计上为了灵活性考虑多加入一两个indirection实在是不算什么。

如果设计时就考虑indirection的效率问题,那么对接口编程根本就实施不开。ioc也最好变成直接new一个实现了。

几点意见:
1。对command的parse,应该在进入整体框架之前,就做好。你这个Command还带有一个命令字符串,这还是一个很原始的命令,肯定应该是command parser内部的数据结构,不应该公开到外部。我前面给的visitor的例子,里面的action已经是经过parse的纯逻辑的entity,和command语法和网络链接是完全没有耦合的。

2。用字符串来标识每一个command也不够灵活。每一个command应该是自描述的,不应该依赖外界的command name。

3。还是应该花心思在每个接口的需求和依赖关系,耦合上。我不同意banq的借鉴struts的意见。实际上,struts作为一个web framework,我认为在这里的可借鉴的意义很小。
view和model当然要分开。但是,mud的view逻辑很简单,不过就是一些描述文字,所以,不要把问题复杂化。

谢谢板桥和ajoo 的意见