0

要求:用java中的socket API实现一个TCP服务器,服务器可以同时处理多个客户端。服务器可以在 XML 文本文件中添加/删除 ... 项目(作为数据存储) 客户端可以向服务器发送命令,例如“addItem”/“removeItem” .. 如果客户端发送了“addItem”,则应将新节点添加到 XML 文档中。

应该使用命令设计模式吗?如果是这样,哪个应该是命令,接收者,调用者,客户端?

我的实现如下(评论中还有一些问题):

interface Command{
    public void execute();
}

AddItemCommand implements Command{

    //The receiver
    XMLFileHelper xmlHelper;

    //The data need to added to XML file
    //This data should be here or not??
    ItemNode addedNode;

    public AddItemCommand(XMLFileHelper newHelper){
        xmlHelper = newHelper;
    }

    public void execute(){
        xmlHelper.addItemNode(addedNode);
    }

}

/*this class will handle all the xml doc operation: add, remove ...
i guess, it is should be the receiver
DataHelper in the parent interface, subclass could be XMLFileHelper,  DBHelper,         MessageQueueHelper ...
*/
public class XMLFileHelper implements DataHelper{

}

RemoveItemCommand implements Command(){
    //...............
}

/*This is the invoker, i am not sure what should it be, so i do not have a good name for it.*/
public class Invoker{
    Map<String, Command> map = new HashMap<String, Command>();

    public void addCommand(String cmdName, Command c){
        map.put(cmdName, c);
    }


    public void processRequest(String reqName){
        map.get(reqName).execute();
   }

}

TcpServerThread implements Runable{

    public void run(){
        DataHelper xmlPaser = new XMLFileHelper();
        Command cmd1 = new AddItemCommand();
        Command cmd2 = new RemoveItemCommand();

        Invoker invoker = new Involer();
        invoker.addCmd("add", cmd1);
        invoker.addCmd("remove", cmd2);

        String cmdName = getCommandName(socket.getInputStream());

        invoker.processRequest(cmdName);


    }
}
4

2 回答 2

1

I am not sure what is the main question you are trying to ask but my intuition tells my that you are looking for some kind of a confirmation whether your approach is good or not.

So to answer this you should think what are requirements for this app. If you want TcpServer to be central point of your application which is the only side in this transaction that offers some services to clients plus you want centralized storage then this approach is perfectly fine. Your design allows you to easily test almoust all units of your code ( the only exception would be TcpServerThread which is doing too much in run method and it has hidden dependencies. I mean it initializes commands add them to Invoker and performs execution. I would split those to init and run logic and Invoker would be injected from outside. The job of invoker's setup would be performed somewhere else )

I think command pattern fits here very well. You could also consider changing your Command interface from:

interface Command{
  void execute();
}

to something like

interface Command{
  ResponseType execute( Parameters params );
}

ResonseType would give you ability to pass feedback to client. And execute with Parameters type injected would give you ability to manage request with parameters.

Now let's focus on Invoker class. You asked about if the name is good. What is the process I always follow when I try to figure out good descriptive name is that I am asking a question: "What is the main purpose of this class and what it is doing ?" In your case Invoker is storing commands ( a CommandRegister ?) in HashMap and also introduces another layer so TcpServer is not calling commands directly. So it feels like another responsibility for the class. Do you really need Invoker to be the one who processRequest ? Do you plan to perform some extra work before command is executed ? If no then maybe it would be good to replace Invoker with something like this:

class CommandRegister{
    Map<String, Command> map = new HashMap<String, Command>();

    public void addCommand(String cmdName, Command c){
        map.put(cmdName, c);
    }


    public Command getCommand(String reqName){
        return map.contains(reqName) ? map.get(reqName) : null;
   }
}

then your TcpServer would call execute directly. What is the benefit ? If you would like to refactor your execute and provide parameters to it you will have one spot less to modify. One spot less to test. And there will be no additional responsibility for the class. Also the name is now more descriptive. You can always have another layer in future that your TcpServer will use to execute commands. For Example:

class CommandInvoker{
  public CommandInvoker(CommandRegister cR){
    this.cR = cR;
  }

  public void execute(String cmdName){
     Command cmd = cR.get(cmdName);
     cmd.execute(); //if you decided like me to return null if there is no Command with specific name in register you should also check if cmd is not null before calling execute.
  }
}

But you can always stay with your Invoker if your app is a simple one and you don't plan to do extra work on the Command execution layer.

Let me know if something is not clear I'll try to clarify ;)

于 2013-11-05T14:11:17.777 回答
0

@Luke:是的,我正在寻找一些确认。我正盯着java中的命令模式学习,所以我不确定是否应该在这里使用命令模式。我正在尝试搜索更多真实世界的命令模式示例,但只是找到了一些非常简单的演示。所以尝试实现一个简单的TCP服务器。可能稍后我会尝试观看 tomcat 的源代码,以检查他们如何设计和实现服务器,如果他们使用命令模式。所以就像你说的,命令模式适合这里。感谢那。但是我担心command和invoker对象的构造应该放在线程的run方法中,因为当客户端尝试连接时,我打算使用一个线程(带有套接字obj)来处理来自同一线程的所有请求客户。所以只有线程知道客户端发送了哪个命令。

于 2013-11-06T06:04:43.280 回答