如何用相同的方法“包装”两个类?

时间:2012-07-14 13:31:16

标签: java oop design-patterns

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

其中一个课程:

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);
        }
    }
} 

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

6 个答案:

答案 0 :(得分: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一样。

答案 1 :(得分:2)

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

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); }
  }
}

答案 2 :(得分: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

答案 3 :(得分:0)

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

我有另一个提议,可能有所帮助:创建一个包装类,它具有针对每种类型的特定函数...它主要是copypaste,但它会强制您使用键入的东西作为参数

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());

答案 4 :(得分:0)

你可能可以利用外观和反射 - 我认为它简化了你访问遗产的方式,也是可扩展的!

class facade{

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

}

答案 5 :(得分:0)

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

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

我的想法是,我可以使用其他代码来使异常语句更有用,并且我不必为每个日志记录框架重复这些代码。包装器不能消除代码重复。消除代码重复不需要编写调用包装器和底层类的代码的多个版本。见https://bradleyaross.wordpress.com/2016/05/05/java-logging-frameworks/

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