4

我正在尝试编写一个框架来处理与外部库及其 API 的接口。作为其中的一部分,我需要在许多 (70ish) 可能的消息类中的每一个中填充一个具有相同名称和类型的标头字段。不幸的是,不是让每个消息类都派生自一个包含标头字段的公共基类,而是每个消息类都是完全独立的。

作为玩具示例:

public class A
{
   public Header header;
   public Integer aData;
}

public class B
{
   public Header header;
   public Long bData;
}

如果他们在 A 和 B 派生自包含标头的某个基类的地方合理地设计了它们,我可以这样做:

public boolean sendMessage(BaseType b)
{
   b.header = populateHeader();
   stuffNecessaryToSendMessage();
}

但就目前而言,Object 是唯一的通用类。我想到的各种选择是:

  • 每种类型都有一个单独的方法。这会奏效,而且速度很快,但代码重复会令人沮丧地浪费。
  • 我可以对每种类型进行子类化,并让它们实现一个通用接口。虽然这可行,但创建 70 多个子类然后修改代码以使用它们而不是原始消息传递类是一个太过分的桥梁。
  • 反射。可行,但我希望它太慢(这里的性能是一个问题)

鉴于这些,每种方法的单独方法似乎是我最好的选择,但我希望有更好的选择。

4

3 回答 3

2

我建议你以下。创建一组您想要的接口。例如

public interface HeaderHolder {
    public void setHeader(Header header);
    public Header getHeader();
}

我希望你的类实现它们,即你喜欢你的类B被定义为

class B implements HeaderHolder {...}

不幸的是,事实并非如此。现在问题!

创建门面:

public class InterfaceWrapper {
    public <T> T wrap(Object obj, Class<T> api) {...}
}

您可以在此阶段使用动态代理来实现它。是的,动态代理使用反射,但现在忘记这一点。

完成后,您可以使用InterfaceWrapper以下内容:

B b = new B();
new IntefaceWrapper().wrap(b, HeaderHolder.class).setHeader("my header");

正如您现在所看到的,您可以将标题设置为您想要的任何类(如果它具有适当的属性)。完成后,您可以检查您的表现。当且仅当在动态代理中使用反射是一个瓶颈,将实现更改为代码生成(例如,基于自定义注释、包名称等)。有很多工具可以帮助您做到这一点,或者您可以自己实现这样的逻辑。关键是您可以随时更改实现IntefaceWrapper而不更改其他代码。

但要避免过早优化。如今,反射非常有效。Sun/Oracle 努力实现这一目标。例如,它们动态创建类并缓存它们以使反射更快。因此,考虑到反射调用的全部流程可能不会花费太多时间。

于 2013-05-16T17:36:28.870 回答
1

我所知道的唯一一个可以做这个Dozer的图书馆。它确实使用了反射,但好消息是测试它是否很慢比编写自己的反射代码来发现它很慢更容易。

默认情况下,dozer 将在两个对象上调用相同的 getter/setter,即使它们完全不同。不过,您可以以更复杂的方式对其进行配置。例如,您也可以告诉它直接访问字段。您可以给它一个自定义转换器来将 Map 转换为 List,诸如此类。

你可以只取一个填充实例,或者甚至你自己的实例,BaseType然后说,dozer.map(baseType, SubType.class);

于 2013-05-16T17:28:26.980 回答
1

在项目构建时动态生成这 70 多个子类怎么样?这样,您就不需要维护 70 多个源文件,同时保留第二个项目符号的方法的好处。

于 2013-05-16T17:34:59.143 回答