API design regarding file tokenization, object value setting and enums of token positions

淺唱寂寞╮ 提交于 2019-12-24 09:50:08

问题


In my Android application I need to read a large amount of data from a set of .ini files that were originally (and still are) deployed with a Windows application. The application is to do with a particular piece of hardware, and the basic purpose of the .ini files is to describe programming constants, real-time data, and UI elements regarding the hardware device.

To move onto the specific Java design question I would like assistance on, here's an example of how a typical row of data from an .ini file would be structured. Typically we have a parameter name, followed by various columns of string or numeric data:

example = example,  "example",  "C",  0,   255,  -1,    -1,  256,  256 

Each row of data that I parse and tokenize is then represented within the application as an object. To perform creation of each object from each row of .ini file data, I am presently using the Builder/Fluent interface. So, the above row would lead to an object being created like so:

someObject.setName( t.s(2) ).setUnits( t.s(3) ).setLowerUpper( t.f(4), t.f(5) ) ...

t is my tokenizer class instance that has been used to parse the .ini file, and its methods s(int) and f(int) are getters that fetch an integer or float for the given column number.

This is a little bit messy, because my basic .ini file processing class that performs object creation based upon data returned from the tokenizer has to contain magic numbers all over the place for the row columns. I also have to take care to use s() or f() as appropriate (although the compiler will of course report an error if I use the wrong one, based upon the types required by my object setters).

What I would really like to do is have another class which I might call IniFileDefinitions. Ideally, that class would contain enumerated definitions for the row numbers and their associated types. Therefore, using this class, the above object creation might look something similar to this (pseudo-code, obviously):

someObject.setName( t.get(INI_NAME) ).setUnits( t.get(INI_UNITS) ).setLowerUpper( t.get(INI_LOWER), t.get(INI_UPPER) );

Where I'm a little bit stuck on is how I can define an enum value which I could pass to my tokenizer, and have the tokenizer not only use that enum to determine the column number, but also vary the type returned (integer, string, float, etc.) based upon that enum.

I realise that Java enums are quite powerful, and it would be possible for me to have two fields per enum, where the first field gives the row number and the second field describes the type, e.g.:

public enum someIniEnumDefs{
   INI_NAME( 2, INI_TYPE_STRING )
   ...

It occurred to me that a way to make this work would be to have a getter method in my tokenizer which could vary the type it returns depending on what type the enum says it should be, but obviously this is not possible in Java. As far as I know, I would have to return a custom object that contains one of all types (string, integer, ... ) or just simply return Object (ugh). I don't particularly like either of those solutions.

One idea I had was that the tokenizer could have multiple (overloaded) value get() methods, each method returning a different primitive type. Which particular get() method would be invoked could depend, somehow, on my 'type' field in the enum definitions. I don't think there's any way I could do that. The only way I have thought of would be to split my enums into several groups representing the different types, and then my tokenizer get methods would be overloaded like so:

int get(SomeIniEnumDefs_Ints){ ... return someInt }
String get(SomeIniEnumDefs_Strings){ ... return someString }
...

Given my lack of experience with Java enums (which I know to be far, far more powerful than what I'm used to in plain old C!) I know there must be a neat way I could achieve this. Can anyone offer any further guidance please?

Thanks,

Trev


回答1:


What you are trying to do is not possible with Java enums. However it could be easily achieved in the other way:

public class IniParam<T> {

  public static final IniParam<String> NAME = new IniParam<String>(2);

  public static final IniParam<String> UNITS = new IniParam<String>(3);

  public static final IniParam<Integer> LOWER = new IniParam<Integer>(4);

  public static final IniParam<Integer> UPPER = new IniParam<Integer>(5);

  private final int position;

  private IniParam(int position) {
    this.position = position;
  }

  public int getPosition() {
    return position;
  }    
}

Tokenizer then may look like:

public class Tokenizer {

  public String get(IniParam<String> iniParam) {
    int position = iniParam.getPosition();
    //...
    return "some string from .ini";
  }

  public int get(IniParam<Integer> iniParam) {
    // ... 
    // return some integer from .ini
  }
}

Usage example:

    Tokenizer t = new Tokenizer();
    String name = t.get(IniParam.NAME);
    int lower = t.get(IniParam.LOWER);
    someObject.setName( t.get(IniParam.NAME) ).setUnits( t.get(IniParam.UNITS) ).setLowerUpper( t.get(IniParam.LOWER), t.get(IniParam.UPPER) );

 

UPDATE

Unfortunately Tokenizer class I provided above will not compile with JDK 7/Eclipse 3.6+ compilers (I can't check it myself right now but the fixes for the following Oracle and Eclipse bugs suppose compilation errors in get(...) methods). If you face this issue here is the workaround:

public class IniParam<T> {

  public static final IniParam<String> NAME = new IniParam<String>(2, String.class);

  public static final IniParam<String> UNITS = new IniParam<String>(3, String.class);

  public static final IniParam<Integer> LOWER = new IniParam<Integer>(4, Integer.class);

  public static final IniParam<Integer> UPPER = new IniParam<Integer>(5, Integer.class);

  private final int position;

  private final Class<? extends T> type;

  private IniParam(int position, Class<? extends T> type) {
    this.position = position;
    this.type = type;
  }

  public int getPosition() {
    return position;
  }

  public Class<? extends T> getType() {
    return type;
  }
}


public class Tokenizer {

  public <T> T get(IniParam<T> iniParam) {
    int position = iniParam.getPosition();
    Class<? extends T> type = iniParam.getType();
    if (type == String.class) {
      //...
      return type.cast("some string from .ini");        
    } else if (type == Integer.class) {
      //...
      // Integer result = ...;
      return type.cast(result);
    } else {
      throw new IllegalArgumentException("Unexpected IniParam type: " + type);
    }      
  }
}


来源:https://stackoverflow.com/questions/6641723/api-design-regarding-file-tokenization-object-value-setting-and-enums-of-token

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!