代码之家  ›  专栏  ›  技术社区  ›  mik01aj

读/写二进制结构:如何简化这段代码?

  •  1
  • mik01aj  · 技术社区  · 15 年前

    我正在编写一个网络应用程序,它发送和接收许多不同类型的二进制数据包,我正在努力使向我的应用程序添加新类型的数据包尽可能容易。

    现在,我创建了一个 Packet 类,我为每种不同的包创建它的子类。然而,它并不像看上去那么干净;我最终得到了如下代码:

    static class ItemDesc extends Packet {
        public final int item_id;
        public final int desc_type;
        public final String filename;
        public final String buf;
    
        public ItemDesc(Type t, int item_id, int desc_type, String filename, String buf) {
            super(t); // sets type for use in packet header
            this.item_id = item_id;
            this.desc_type = desc_type;
            this.filename = filename;
            this.buf = buf;
        }
    
        public ItemDesc(InputStream i) throws IOException {
            super(i); // reads packet header and sets this.input
            item_id = input.readInt();
            desc_type = input.readByte();
            filename = input.readStringWithLength();
            buf = input.readStringWithLength();
        }
    
        public void writeTo(OutputStream o) throws IOException {
            MyOutputStream dataOutput = new MyOutputStream();
            dataOutput.writeInt(item_id);
            dataOutput.writeByte(desc_type);
            dataOutput.writeStringWithLength(filename);
            dataOutput.writeStringWithLength(buf);
            super.write(dataOutput.toByteArray(), o);
        }
    }
    

    这种方法困扰我的是代码重复——我重复了四次包结构。我很乐意避免这种情况,但我看不出一个合理的方法来简化它。

    如果我是用python编写的,我将创建一个包含所有可能字段类型的字典,然后像这样定义新的数据包类型:

    ItemDesc = [('item_id', 'int'), ('desc_type', 'byte'), ...]
    

    我想我可以用任何功能语言做类似的事情。但是,我看不出一种方法来使用Java。

    (也许我太学究了,或者我习惯了函数式编程和编写代码,这样我就可以避免重复了。)

    提前感谢您的建议。

    3 回复  |  直到 14 年前
        1
  •  1
  •   Stephen C    15 年前

    我同意@silky,您当前的代码是一个很好的解决方案。在我看来,一点重复(尽管不是重复)的代码也不是坏事。

    如果您想要一个更类似于Python的解决方案,您可以:

    1. 用某种保留顺序的映射结构替换itemdesc的成员属性,使用 writeTo 迭代映射的方法。您还需要为每个属性添加getter,并替换现有字段的所有用途。
    2. 用属性对象替换成员属性,并使用属性序列化而不是二进制写入。
    3. 写一个共同点 写成 方法,使用Java反射来访问成员属性及其类型并序列化它们。

    但在这三种情况下,代码将比当前的“丑陋”代码慢、复杂,而且可能更脆弱。我不会这么做的。

        2
  •  0
  •   Noon Silk    15 年前

    我觉得没问题。您可能只是想在继承链上抽象数据包的一些“常规”部分,因此不需要读取它们,但是像您这样重复格式是有意义的,因为您有一个案例可以从构造函数中读取原始数据、从流中读取数据和写入数据。我看不出有什么问题。

        3
  •  0
  •   Vitaliy    15 年前

    我不确定你可以用Java做这件事,但是也许你可以重用其中的一个:

    public ItemDesc(InputStream i) throws IOException {
        super(i); // reads packet header and sets this.input
    
        this(input.readInt(), input.readByte(), input.readStringWithLength(), input.readStringWithLength());
    }
    

    如果“this”意味着调用这个类ctor,那么语法可能是什么。