Am I abusing/misusing Java reflection?
I'm writing a program to read data from a file, which may be in one of several format (different versions of the same format, actually) and I'm using reflection for calling the appropriate function for each format. Assuming that file format is a number specified on the first byte of the file:
Class DataFile extends Model {
...
Blob file
...
public void parse() throws Exception{
InputStream is = file.get();
Class c = Class.forName("models.DataFile");
Method m = c.g开发者_StackOverflowetMethod("parse_v"+is.read(), (Class []) null);
m.invoke(this, (Object []) null);
}
public void parse_v0() throws Exception{
...
}
public void parse_v1() throws Exception{
...
}
}
My question is, am I abusing/misusing reflection? I have the feeling that I should be using inheritance and create a different class for each file type with its own "parse" procedure, but I don't know the file type until I start parsing... and then I cannot "downcast" and just use something like ((DataFile_v1) this).parse()
so I am a little lost.
Thank you for your time!
There's nothing fundamentally wrong with this, but a more flexible and extensible way to do the same thing would be to use the version information as a key in a Map
, and have the values in the Map
be handler objects. Then any code can register a handler (the handlers can all implement a common interface) and your reader code can just look up the handler in the Map
and invoke it.
Be sure to handle the case where the Map
doesn't include a handler for a particular version!
If you make a DataFile
interface define a parse
method, and implement the interface with multiple classes (DataFile_v1
, etc.), then the calling code doesn't have to know which implementation was chosen.
DataFile dataFile = dataFileFactory.getForVersion(is.read());
dataFile.parse(file);
I'd argue that this is a better approach from a general design perspective. However, at some point you will need to create some kind of mapping between the version number and the DataFile implementations. (In this case I'm doing it in an imaginary dataFileFactory
.) You'll have to determine whether it would be more appropriate to select an implementation using reflection or some other method.
I think it's OK to use reflection here. The alternative would be using inheritance or an enum (i.e. the Strategy pattern), and a map from the version code to the proper Strategy. Once you have initialized all the desired mappings, you just get the right parser object from the map and invoke it. However, setting up this solution still requires a significant amount of boilerplate code, which diminishes its readability.
What you're doing isn't bad. If you want to have the different parsers in different classes, you can't downcast as you say, but you could instantiate a new parser object. So your existing class would be a facade in front of the actual parsers which aren't instantiated until you know which format you're parsing.
You can use a collection, but using reflections is looking up a collection as well. Provided your mapping doesn't change I would use reflections.
getClass().getMethod("parse_v"+is.read()).invoke(this);
精彩评论