【问题标题】:How to split the implementation of an interface between two classes如何在两个类之间拆分接口的实现
【发布时间】:2013-07-13 21:50:56
【问题描述】:

我有一个由带有 getter/setter 方法的接口组成的对象模型。这些对象的实现是使用动态代理创建的,其中隐含字段的值(使用 JavaBean 命名约定)存储在 Map 中。

我想为这些接口添加方法以提供业务逻辑(你知道,就像一个真实的对象模型,而不仅仅是 POJO 的集合)。

我的第一个想法是创建实现每个接口但只提供业务方法的实现的抽象类。然后我会将这些实现与 InvocationHandler 中的 Map 一起使用,以提供接口的完整实现。

类似:

interface ModelObject extends BaseModel {
    void setFoo(String foo);
    String getFoo();

    void doSomething();
}

public abstract class ModelObjectImpl implements ModelObject {

    @Override
    public void doSomething()
    {
        // Do something
    }
}

public class ModelObjectInvocationHander implements InvocationHandler {

    Map<String, Object> fieldValues; // holds values for implied fields for getter setter
    ModelObject modelObject;         // holds reference to object implementing business methods

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // Get implied field name for method name by removing "get"/"set" and lower casing next letter
        String fieldName = getBeanNameForMethod(method.getName());

        if (fieldValues.containsKey(fieldName)) {
            return fieldValues.get(fieldName);
        }

        // Not a getter/setter so must be a business method.  Delegate to implementation class
        return method.invoke(modelObject, args);
    }
}

除了我不能创建抽象类的实例之外,这样的东西(但显然更复杂)会起作用。我可以使 BusinessObjectImpl 非抽象并添加永远不会调用的 getter/setter 方法的无操作实现,但这只会使代码变得丑陋并导致维护问题。我也可以让 BusinessObjectImpl 实际上不实现 BusinessObject 接口,但是这会破坏实现和接口之间的良好绑定,从而在接口和“实现”不同步时导致错误。

有没有什么鬼鬼祟祟的 Java 反射技巧可以用来调用这些业务方法?

更新: 结合现有的 Java 动态代理框架和Javassist 为抽象实现类创建代理。这允许在根据需要添加业务方法之前对现有模型接口完全没有任何更改。现在已经具备向对象添加行为的能力。现在由开发人员开始编写真正的面向对象代码。

public class ModelObjectInvocationHandler implements InvocationHandler
{

    public ModelObjectInvocationHandler(Class<ModelImplementation<? extends BaseModel>> implementationClass)
    {
        if (implementationClass != null) 
        {
            ProxyFactory factory = new ProxyFactory();
            factory.setSuperclass(implementationClass);
            try
            {
                modelObject = (ModelObject) factory.create(new Class<?>[0], new Object[0]);
            }
            catch (Exception e)
            {
                // Exception handling
            }
        }
    }

    Map<String, Object> fieldValues; // holds values for implied fields for getter setter
    ModelObject modelObject;         // holds reference to object implementing business methods

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {

        // Get implied field name for method name by removing "get"/"set" and lower casing next letter
        String fieldName = getBeanNameForMethod(method.getName());

        if (fieldValues.containsKey(fieldName))
        {
            return fieldValues.get(fieldName);
        }

        // Not a getter/setter so must be a business method.  Delegate to implementation class
        if (modelObject != null)
        { 
            return method.invoke(modelObject, args);
        }

        return null;
    }
}

在运行时,我会扫描实现类并创建一个Map&lt;Class&lt;? extends BaseModel&gt;, Class&lt;ModelImplementation&gt;&gt;。在为接口创建动态代理时,我在映射中找到它的实现类,并将其传递给InvocationHandler。任何与 bean 名称不匹配的方法都将委托给实现类的代理。当然,这比这要复杂一些,因为我必须考虑模型接口中的类层次结构和多重继承,但理论是合理的。

【问题讨论】:

    标签: java reflection dynamic-proxy


    【解决方案1】:

    我不知道有任何标准的 Java 反射技巧可以做到这一点。您可以在类加载时使用cglibjavaassist 动态扩展抽象类。这会稍微提高性能,因为不再需要 Proxy 对象。相反,您可以在创建新类时直接实现 getter/setter 方法。

    没有这些技巧的第三种方法是使用委托模式:

    public class ModelObjectImpl implements ModelObject {
      private final ModelObject delegate;
    
      public ModelObjectImpl(ModelObject delegate) { 
        this.delegate = delegate;
      }
    
      @Override
      public void doSomething() { /* Do something */ }
    
      @Override
      public String getFoo() { return delegate.getFoo(); }
    
      @Override
      public void setFoo(String foo) { delegate.setFoo(foo); }
    }
    

    将实现接口的 getter/setter 方法的代理提供给构造函数 delegate 参数。然而,虽然这看起来比存根方法(至少对我而言)更好,但它仍然是重复的代码。所以如果你真的想拥有这样的动态类,那就去动态字节码生成吧。

    参考资料:

    【讨论】:

    【解决方案2】:

    一种方法是在新界面中定义所有“附加”或“业务”方法的合同,例如:

    interface ModelObjectExtension {
        void doSomething();
    }
    
    interface ModelObject extends ModelObjectExtension {
        void setFoo(String foo);
        String getFoo();
    }
    
    public abstract class ModelObjectExtensionImpl implements ModelObjectExtension {
    
        @Override
        public void doSomething()
        {
            // Do something
        }
    }
    
    public class ModelObjectImpl extends ModelObjectExtension { 
        // whatever your current implementation is...
    }
    

    最后你可以在你的处理程序中使用以下来调用扩展方法:

    ((ModelObjectExtension) modelObject).doSomething();
    

    【讨论】:

    • 很有趣,但现在您需要在两个地方查找业务对象的方法。此外,由于我在上面给出的相同原因,您不能将 ModelObjectExtensionImpl 设为 abstract。当然,在您的示例中 is 实际上并不需要是抽象的。
    猜你喜欢
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 2015-09-01
    相关资源
    最近更新 更多