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

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

首先,客户端发送一个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()行为又要添加一串,应该怎么重构或设计才好???

jrog
2004-02-06 14:37
你可不可以贴一个sequence diagram或者是collaboration diagram出来呀?这样绕来绕去都不明白你想实现什么!呵呵

ajoo
2004-02-06 16:21
呵呵,类似一个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){
    ......
  }
  ...
}
<p>

主循环接受玩家发来的消息,解释成一个一个的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来做这个事情。

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

ajoo
2004-02-07 00:30
Command最好不要直接调用Player.talkTo(NPC).

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

至于如何解释执行这些action,应该交给一个单独的visitor模块。

这样,给出不同的visitor模块,你可以实现完全不同的逻辑。

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

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

banq
2004-02-07 11:07
你这个设计其实是用了职责链模式,这是很耗费性能的。

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

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

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

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

to ajoo:

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

zard222
2004-02-08 20:51
关于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 ) ;

}

}

zard222
2004-02-09 09:26
player不是只能和npc交谈,如果和玩家交谈就是私聊,npc和humanPlayer继承至player,实现talkto(),

板桥说的职责链压缩,不太明白怎么压缩

rypan
2004-02-09 11:04
好像所有的mud都是使用心跳模式的吧?

banq
2004-02-09 12:16
如果你的客户端发送到服务器端是编号的,如1代表play 2代表stop等,

那么压缩成Command模式就方便多。

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

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

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

ajoo
2004-02-09 13:03
我的意思是说,在java里面,对效率不能这么个计较方法。

其实,所以职责链效率低,不过是因为消息经过了若干次indirection。但是,OO里面,decorator, adaptor, bridge等等等等,几乎所有的pattern都是通过加入一个额外的indirection来获得灵活性的。

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

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

ajoo
2004-02-09 13:53
几点意见:

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逻辑很简单,不过就是一些描述文字,所以,不要把问题复杂化。

zard222
2004-02-10 10:52
谢谢板桥和ajoo 的意见

猜你喜欢
2Go 1 2 下一页