我正在编写一个程序从文件中读取数据,该文件可能是几种格式之一(实际上是同一格式的不同版本),并且我正在使用反射为每种格式调用适当的函数。假设文件格式是文件第一个字节上指定的数字:
Class DataFile extends Model {
...
Blob file
...
public void parse() throws Exception{
InputStream is = file.get();
Class c = Class.forName("models.DataFile");
Method m = c.getMethod("parse_v"+is.read(), (Class []) null);
m.invoke(this, (Object []) null);
}
public void parse_v0() throws Exception{
...
}
public void parse_v1() throws Exception{
...
}
}
我的问题是,我滥用/误用反射吗?我有一种感觉,我应该使用继承,并为每个文件类型创建一个不同的类,并使用它自己的"解析"过程,但我不知道文件类型,直到我开始解析…然后我不能"downcast",只使用像((DataFile_v1) this).parse()
这样的东西,所以我有点迷路了。
这并没有什么本质上的错误,但是做同样事情的更灵活和可扩展的方法是将版本信息用作Map
中的键,并将Map
中的值作为处理程序对象。然后,任何代码都可以注册处理程序(处理程序都可以实现一个公共接口),您的读者代码可以在Map
中查找处理程序并调用它。
一定要处理Map
不包含特定版本的处理程序的情况!
如果您创建一个DataFile
接口,定义一个parse
方法,并使用多个类(DataFile_v1
等)实现该接口,则调用代码不必知道选择了哪个实现。
DataFile dataFile = dataFileFactory.getForVersion(is.read());
dataFile.parse(file);
我认为从一般设计的角度来看,这是一种更好的方法。然而,在某些时候,您将需要在版本号和DataFile实现之间创建某种映射。(在这种情况下,我是在一个虚构的dataFileFactory
中做的。)您必须确定使用反射还是其他方法来选择实现是否更合适。
我认为在这里使用反射是可以的。另一种选择是使用继承或枚举(即策略模式),以及从版本代码到适当策略的映射。一旦初始化了所有所需的映射,就可以从映射中获得正确的解析器对象并调用它。然而,设置这个解决方案仍然需要大量的样板代码,这会降低它的可读性。
你做的还不错。如果您希望在不同的类中使用不同的解析器,则不能像您所说的那样向下转换,但可以实例化一个新的解析器对象。因此,您现有的类将是一个门面,在实际的解析器之前,它不会实例化,直到您知道您正在解析哪种格式。
您可以使用集合,但使用反射也是查找集合。如果你的映射不改变,我将使用反射。
getClass().getMethod("parse_v"+is.read()).invoke(this);