9

我必须处理两个具有相同方法的类,但它们没有实现相同的接口,也没有扩展相同的超类。我不能/不允许更改此类,并且我不构造此类的实例,我只获取此类的对象。避免大量代码重复的最佳方法是什么?

一类:

package faa;

public class SomethingA {

    private String valueOne = null;
    private String valueTwo = null;

    public String getValueOne() { return valueOne; }
    public void setValueOne(String valueOne) { this.valueOne = valueOne; }

    public String getValueTwo() { return valueTwo; }
    public void setValueTwo(String valueTwo) { this.valueTwo = valueTwo; }
}

和另外一个...

package foo;

public class SomethingB {

    private String valueOne;
    private String valueTwo;

    public String getValueOne() { return valueOne; }
    public void setValueOne(String valueOne) { this.valueOne = valueOne; }

    public String getValueTwo() { return valueTwo; }
    public void setValueTwo(String valueTwo) { this.valueTwo = valueTwo; }
}

(实际上这些类更大)

我现在唯一的想法是创建一个包装类:

public class SomethingWrapper {

    private SomethingA someA;
    private SomethingB someB;

    public SomethingWrapper(SomethingA someA) {
        //null check..
        this.someA = someA;
    }

    public SomethingWrapper(SomethingB someB) {
        //null check..
        this.someB = someB;
    }

    public String getValueOne() {
        if (this.someA != null) {
            return this.someA.getValueOne();
        } else {
            return this.someB.getValueOne();
        }
    }

    public void setValueOne(String valueOne) {
        if (this.someA != null) {
            this.someA.setValueOne(valueOne);
        } else {
            this.someB.setValueOne(valueOne);
        }
    }

    public String getValueTwo() {
        if (this.someA != null) {
            return this.someA.getValueTwo();
        } else {
            return this.someB.getValueTwo();
        }
    }

    public void setValueTwo(String valueTwo) {
        if (this.someA != null) {
            this.someA.setValueTwo(valueTwo);
        } else {
            this.someB.setValueTwo(valueTwo);
        }
    }
} 

但我对这个解决方案并不满意。有没有更好/更优雅的方法来解决这个问题?

4

6 回答 6

7

更好的解决方案是创建一个接口来表示两个类的统一接口,然后编写两个实现该接口的类,一个包装 A,另一个包装 B:

public interface SomethingWrapper {
    public String getValueOne();
    public void setValueOne(String valueOne);
    public String getValueTwo();
    public void setValueTwo(String valueTwo);
};

public class SomethingAWrapper implements SomethingWrapper {

    private SomethingA someA;

    public SomethingWrapper(SomethingA someA) {
        this.someA = someA;
    }

    public String getValueOne() {
        return this.someA.getValueOne();
    }

    public void setValueOne(String valueOne) {
        this.someA.setValueOne(valueOne);
    }

    public String getValueTwo() {
        return this.someA.getValueTwo();
    }

    public void setValueTwo(String valueTwo) {
        this.someA.setValueTwo(valueTwo);
    }
};

然后另一个类就像SomethingBWrapper一样。

于 2012-07-14T13:33:36.873 回答
2

在那里,一个鸭式解决方案。这将接受任何具有valueOne,valueTwo属性的对象,并且可以轻松扩展为进一步的道具。

public class Wrapper
{
  private final Object wrapped;
  private final Map<String, Method> methods = new HashMap<String, Method>();
  public Wrapper(Object w) {
    wrapped = w;
    try {
      final Class<?> c = w.getClass();
      for (String propName : new String[] { "ValueOne", "ValueTwo" }) {
        final String getter = "get" + propName, setter = "set" + propName;
        methods.put(getter, c.getMethod(getter));
        methods.put(setter, c.getMethod(setter, String.class));
      }
    } catch (Exception e) { throw new RuntimeException(e); }
  }
  public String getValueOne() {
    try { return (String)methods.get("getValueOne").invoke(wrapped); }
    catch (Exception e) { throw new RuntimeException(e); }
  }
  public void setValueOne(String v) {
    try { methods.get("setValueOne").invoke(wrapped, v); }
    catch (Exception e) { throw new RuntimeException(e); }
  }
  public String getValueTwo() {
    try { return (String)methods.get("getValueTwo").invoke(wrapped); }
    catch (Exception e) { throw new RuntimeException(e); }
  }
  public void setValueTwo(String v) {
    try { methods.get("setValueTwo").invoke(wrapped, v); }
    catch (Exception e) { throw new RuntimeException(e); }
  }
}
于 2012-07-14T14:05:04.190 回答
1

您可以使用动态代理在您定义的接口和符合但不实现您的接口的类之间创建“桥梁” 。

这一切都始于一个界面:

interface Something {
  public String getValueOne();
  public void setValueOne(String valueOne);
  public String getValueTwo();
  public void setValueTwo(String valueTwo);
}

现在你需要一个InvocationHandler, 它只会将调用转发到与调用的接口方法匹配的方法:

class ForwardInvocationHandler implements InvocationHandler {
  private final Object wrapped;
  public ForwardInvocationHandler(Object wrapped) {
    this.wrapped = wrapped;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    Method match = wrapped.getClass().getMethod(method.getName(), method.getParameterTypes());
    return match.invoke(wrapped, args);
  }
}

然后您可以创建您的代理(将其放在工厂中以便于使用):

SomethingA a = new SomethingA();
a.setValueOne("Um");

Something s = (Something)Proxy.newProxyInstance(
    Something.class.getClassLoader(), 
    new Class[] { Something.class }, 
    new ForwardInvocationHandler(a));

System.out.println(s.getValueOne()); // prints: Um

另一种选择更简单,但需要您对每个类进行子类化并实现创建的接口,就像这样:

class SomethingAImpl extends SomethingA implements Something {}
class SomethingBImpl extends SomethingB implements Something {}

(注意:您还需要创建任何非默认构造函数)

现在使用子类而不是超类,并通过接口引用它们:

Something o = new SomethingAImpl(); // o can also refer to a SomethingBImpl
o.setValueOne("Uno");
System.out.println(o.getValueOne()); // prints: Uno
于 2012-07-14T17:13:23.583 回答
0

我认为你原来的包装类是最可行的选择......但是它可以使用反射来完成,你真正的问题是应用程序是一团糟......反射可能不是你正在寻找的方法

我有另一个建议,这可能会有所帮助:创建一个包装类,它对每种类型的类都有特定的功能......它主要是复制粘贴,但它迫使你使用类型化的东西作为参数

class X{
    public  int asd() {return 0;}
}
class Y{
    public  int asd() {return 1;}
}
class H{
    public  int asd(X a){
        return  a.asd();
        }
    public  int asd(Y a){
        return  a.asd();
        }
}

用法:

System.out.println("asd"+h.asd(x));
System.out.println("asd"+h.asd(y));

我想指出一个接口也可以由祖先实现,如果你正在创建这些类 - 但不能修改它的源,那么你仍然可以从外部重载它们:

public  interface II{
    public  int asd();
}
class XI extends X implements II{
}
class YI extends Y implements II{
}

用法:

II  a=new XI();
System.out.println("asd"+a.asd());
于 2012-07-15T12:22:24.333 回答
0

您可能可以利用外观和反射 - 在我看来,它简化了您访问遗留的方式并且是可扩展的!

class facade{

 public static getSomething(Object AorB){
    Class c = AorB.getClass();
    Method m = c.getMethod("getValueOne");
    m.invoke(AorB);
 }
 ...

}
于 2012-07-17T07:43:29.943 回答
0

我写了一个类来封装日志框架 API。不幸的是,放入这个盒子太长了。

该程序是http://www.github.com/bradleyross/tutorials项目的一部分,文档位于http://bradleyross.github.io/tutorials。模块 tutorials-common 中的 bradleyross.library.helpers.ExceptionHelper 类的代码位于https://github.com/BradleyRoss/tutorials/blob/master/tutorials-common/src/main/java/bradleyross/library/助手/ExceptionHelper.java

这个想法是我可以拥有我想让异常语句更有用的附加代码,并且我不必为每个日志记录框架重复它们。包装器不是您消除代码重复的地方。代码重复的消除在于不必编写调用包装器和底层类的代码的多个版本。见https://bradleyaross.wordpress.com/2016/05/05/java-logging-frameworks/

bradleyross.helpers.GenericPrinter 类是另一个包装器,使您能够编写与 PrintStream、PrintWriter 和 StringWriter 类和接口一起使用的代码。

于 2016-05-22T17:51:16.413 回答