开发者

How can I program a class to support multiple versions of the structure it represents?

Let's say that I want a class that represents a data structure in memory. In this structure, the first two bytes indicate which version of the structure it is. The order and size of the data that follows depends on the version of the structure.

For example: Version 1 is a 10-byte structure that looks like this:

  • Byte offset 0: Format Version (2 bytes)
  • Byte offset 2: Data section A (4 bytes)
  • Byte offs开发者_Go百科et 6: Data section B (4 bytes)

Version 2 is a 20-byte structure that looks like this:

  • Byte offset 0: Format Version (2 bytes)
  • Byte offset 2: Data section A (8 bytes)
  • Byte offset 10: Data section B (6 bytes)
  • Byte offset 16: Data section C (4 bytes)

I want my class to be able to support both versions without the user of the class having to specify which version to use. That is, when the object is constructed, it should be able to use the Format Version field to determine what its structure should be. Then the getters/setters for each field should be created appropriately based on what the structure is. I also will want to be able to add support for additional structure version in the future. Each new version of the structure will most likely contain all the same fields as the older versions, but perhaps with greater allocation lengths for each of these fields, and perhaps some newly added fields.

As far as I can tell, the only constraint that I have is that the format version field always appears at byte offset 0, and is always 2 bytes long.

So, is it possible to accomplish my goal?


Typically, you'd be creating and using separate classes for each different version of your structure. You'd give them a common interface so you could address and store them by a common type. Those different structure classes could then contain different fields and methods for dealing with the different variations of your data.

// The common interface. Every struct knows how to read and write itself.
public interface MultiStruct {
  public void readData(InputStream in);
  public void writeData(OutputStream out);
}

// This class knows only about storing a format code
public abstract class AbstractMultiStruct implements MultiStruct {
  protected static final int FORM1 = 1, FORM2 = 2;

  private int format;

  public AbstractMultiStruct(int fmt) {
    this.format = fmt;
  }

  public int getFormat() {
    return this.format;
  }

}

// This is the first real struct implementation.
public class Struct1 extends AbstractMultiStruct {
  private char[] dataA;
  private char[] dataB;

  public Struct1() {
    super(FORM1);
    this.dataA = new char[22];
    this.dataB = new char[33];
  }

  public void readData(InputStream in) {
    ...
  }

  public void writeData(OutputStream out) {
    ...
  }

  public String toString() {
    ...
  }


}


I think you need a class hierarchy here. Create an abstract class for the concept the structure is implementing. This class has the logic (say a static method) to construct a concrete class from the data, by inspecting the version field. The abstract class then instantiates either a Version1 class or a Version2 class according to the data.

The abstract class contains only the version field. The Version1 subclass contains Version 1 fields. Version 2 subclass contains version 2 fields. Since the two versions are very different, Version 1 and Version 2 both derive from the base class. If Version 2 was an extension of Version 1, then you would have the Version2 class extend the Version1 class.

E.g.

abstract class MyStructure
{
   static public MyStructure create(DataInputStream data) {
      int version = data.readShort();
      MyStructure structure;
      if (version==1)
         structure = new MyStructure1();
      else if (version==2)
          structure = new Mystructure2();
      else
         throw IllegalArgumentException("unkonwn version "+version);
      structure.read(data);
      return structure;
   }

   abstract protected void read(DataInputStream input) throws IOException;

   // accessors for the common sections, either as raw data
   public abstract byte[] getSectionA();
   // or as primitive types, 
   public abstract long getSectionA();
   // or with it's own class
   public abstract SectionA getSectionA();

}

class MyStructureVersion1 extends MyStructure
{
    // fields for version 1
    int a;
    int b;

    protected void read(DataInputStream input) throws IOException { 
      // read fields from input
      a = input.readInt();
      b = input.readInt();
    }

    public long getSectionA() {
        return a;
    }
}


class MyStructureVersion2 extends MyStructure
{
    // fields for version 2, could be primitives, byte arrays 
    // or SectionA, SectionB objects
    long a;
    long b;
    int c;
    protected void read(DataInputStream input) throws IOException { 
      // read fields from input
      a = input.readLong();
      b = input.readLong();
      c = input.readInt();
    }

    pubic long getSectionA() {
        return a;
    }
}


Typically, you would create one class that knows how to interpret the format version bytes and returns the correct implementation of a class that provides access to the data. Those implementations should share a common base class. A very simplified example:

public abstract class MyData {
  protected byte[] data;

  MyData(byte[] data) {
    this.data = data;
  }

  public abstract WhatEver getInformation();
}

public class MyDataA extends MyData {

  MyDataA(byte[] data) {
    super(data);
  }

  public WhatEver getInformation() {
    //extract data from the correct offset for format a
    //in the data field of the base class
  }
}

public class MyDataB extends MyData {

  MyDataB(byte[] data) {
    super(data);
  }

  public WhatEver getInformation() {
    //extract data from the correct offset for format b
    //in the data field of the base class
  }
}

public class MyDataFactory {

  public MyData createMyData(byte[] data) {
    if ( first 2 bytes indicate format a ) {
      return new MyDataA(data);
    } else if (first 2 bytes indicate format b) {
      return new MyDataB(data);
    }
  }

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜