面向java对象的文本字符串协议

时间:2014-05-02 13:29:57

标签: java oop protocols

我正在开发一个应用程序,我想创建一个编译和定义的自定义文本协议。即我想发送表示在基于服务器客户端的系统中采取的信息/操作的文本字符串。

是否有任何参考/最佳实践方法来执行此面向对象(在java中)?

可能发送的实际文本字符串的一些示例,为方便起见用空格分隔(在实际情况下将被其他内容拆分):

command1 <timestamp> <id> <arg1> <arg2>
command2 <timestamp> <id> <arg1> <arg2> <arg3>
command3 subcommand1 <timestamp> <id>
command3 subcommand2 <timestamp> <id>

因此,所有消息都包含一些内容,但在其他方面,消息可能完全不同,具体取决于未来的实现等。

我想要实现的是接收的文本字符串应该使用此协议进行解析,以便使用它的各方只需将接收到的文本字符串传递给协议,然后就可以根据需要切换到不同的功能在消息类型上。我的第一个想法是,是否可以实现基类,让我们说“消息”,然后是更具体的消息类型的子类(例如Register或UpdateSomething)。

伪代码大致解释了我想要实现的目标:

object msg = new Message(rawText);
switch (msg.getType()) {
    case Message.Register:
        // Register logic, data needed would be available in the msg-object.
        // Most likely a function, for instance: handleRegistration(msg.uid, msg.password)
        break;
    case Message.SomeData.Get:
        // logic/function to return SomeData, args/data needed would be available in the msg-object.
        break;
    case Message.SomeData.Update:
        // logic/function to update SomeData, args/data needed would be available in the msg-object.
        break;
    default:
        // Some default logic.
        break;

但我意识到,即使我能够解析rawText并以某种方式将其转换为不同的对象,具体取决于其内容(我该怎么做?),就不可能在一个好的时候打开对象类型方式,我看到许多人认为这不是一个好方法。

我可以通过许多丑陋的方式使这一切工作,这没有问题,我只是非常不确定正确的方法。我真的想学习如何以一种不错的方式做到这一点,同时考虑扩展(协议会增长)。我希望协议只是一个包(jar),每个方(不同的客户端和服务器)可以使用,之后没有人需要打扰通过连接实际发送的(文本)。

非常感谢任何帮助和指导,我愿意接受其他完全不同的道路。请再次注意,但我并没有寻求帮助,只是“让它工作”。我正在询问实施这些内容的最佳实践,或者我和其他人阅读本文的参考资料可以学习如何做到这一点。

谢谢!

修改

我想我遇到的主要问题是问题本身。这些消息彼此之间有很大不同,这意味着如果我希望数据成员是唯一的,我将会在很多类中结束。这本身并不是一个真正的问题,它只是名称必须是独一无二的。我想把它们分成几个层次。

例如,使用REGISTER类型:

REGISTER消息是一种MESSAGE。但是也有不同类型的注册消息:

 REGISTER REQUEST <id> <password>
 REGISTER OK
 REGISTER ERROR <reason>

我想将它们分组,因为它们都是注册消息,但它们也是具有不同有效负载的不同类型的注册消息(即,如果转换为对象,则需要一组不同的成员)。因此,如果我希望一个对象能够从变量中提取这些信息,那么我需要3个类(例如RegisterRequestMessage,RegisterOKMessage,RegisterErrorMessage),它感觉好像所有这些类和名称都有点太多了

我希望实现:

  • 使用该协议的开发人员的可读性和可用性,何时 他们制作他们的开关盒以查看他们收到的消息或 当他们发出新消息时,他们应该很容易(在IDE中 支持它)能够列出他们可以选择的消息类型。

  • 能够从唯一的消息中提取信息 不同的消息类型取决于发送的内容。我想要的 可用于此类消息的数据(再次在IDE中可见) 在使用对象时支持它。

我想这里不会有任何真正顺畅的方式,要么我最终会有很多课程(这个问题实际上是很多长篇名称的消息),或者我最终还是要做它更通用,用数据填充json对象或类似物。

很多课程:

handleRegisterMessage(MyLongNameSpecifiedMessageClass msg) {
    this.id = msg.id;
    this.password = msg.password;
    // etc etc
}

更通用的json或类似的东西:

handleRegisterMessage(JSONObject msg) {
    this.id = msg.get("id");
    this.password = msg.get("password");
    // etc etc
}

我不确定是否还有更多工作要做,但如果你们有更多经验的人在这里看到一些更优雅或更简单的解决方案,我就不会尝试。

编辑2:

决定为每种消息类型使用一个唯一的类,即使会有相当多的消息,名称可能会有点长/丑。感觉就像是最封装且易于使用的方式。它将被编译成一个jar文件,无论如何都会以这种方式使用。感谢所有建议,因为其他人可能会决定采取其他途径。

3 个答案:

答案 0 :(得分:0)

如果我理解你的意思,基本上,信息必须是

  • 从字符串中解析,
  • 习惯做某事。

所以,你可能会有像

这样的东西
abstract class Message {
    public abstract void doSomething();
    private String myType; // or enum, or whatever

    public String getMyType() {
        return myType;
    }

    public static Message parse(String s) {
        // parse s
        // if s contains command 'register', return a new RegisterMessage
        // if s contains command 'someDataGet', return a new SomeDataGetMessage

        return // a newly created message;
        }
    }

    class MessageRegister extends Message {

    @Override
    public void doSomething() {
        // do what a Register message does          
    }

    public MessageRegister() {
        myType = "REGISTER";
    }

}

class MessageSomeDataGet extends Message {

    @Override
    public void doSomething() {
        // do what a SomeDataGet message does       
    }

    public MessageSomeDataGet() {
        myType = "SOMEDATAGET";
    }
}

然后,使用

创建新消息
Message m = Message.parse("some command");

您可以像这样打开消息类型:

switch (m.getMyType()) {
    case "SOMEDATAGET":
        // whatever
}

答案 1 :(得分:0)

我通常这样做的方法是使用接口,让我们称之为ConsoleProgram,它接受​​字符串参数和将字符串耦合到程序的HashMap。基本上做类似于你的switch语句的事情,然后每个程序在传递其余的字符串时都会担心它自己的格式(不包括选择字符串部分的第一个程序)

public interface ConsoleProgram {
    public String getProgramName(); //what the command string starts with to call this program
    public void runProgram(String[] arguments);
}

然后一个班级可以处理接收字符串并将其传递给ConsoleProgram,而不必担心ConsoleProgram实际上做了什么

public class Console{

    public static HashMap<String, ConsoleProgram> availablePrograms=new HashMap<>();


    public void registerProgram(ConsoleProgram program){

        availablePrograms.put(program.getProgramName().toLowerCase(), program);

    }


    public void recieveCommand(String command){

        String[] parts = command.split(" ");

        ConsoleProgram program=availablePrograms.get(parts[0].toLowerCase());

        if (program!=null){
            System.out.println(
                    program.runProgram(Arrays.copyOfRange(parts, 1, parts.length));

        }else{
            System.out.println("program unavailable");
            System.out.println(
                availablePrograms.get("availableprograms").runProgram(parts, this, null)
            );
        }
    }

}

这是我自己用来支持我正在开发的游戏中的管理功能,它可以很好地扩展。此外,如果ConsoleProgram需要访问您不需要Console了解它们的异常内容,那么特定ConsoleProgram可以在使用{注册之前 - 在其构造函数中获取这些对象{1}}

答案 2 :(得分:0)

我建议使用一种专门为此创建的知名工具,而不是编写自己的序列化器/ parcer。

我最喜欢的一个是谷歌的protocol buffers,还有很多其他人。

使用协议缓冲区,您需要定义自定义消息格式(查看简单的java tutorial以供参考),然后为其生成Java代码。

你可以在服务器/客户端进行,或者,我想你可以简单地在库中包含生成的文件和依赖jar,并在服务器/客户端之间共享。

这将带来许多好处:

  • 您不需要编写序列化程序/解析器(更重要的是花时间调试它!)
  • 它非常灵活,您可以在以后更改/扩展您的邮件
  • 您不仅限于Java实现,使用C ++ / Python编写的其他服务器/客户端也可以理解相同的消息格式。