9

同じメソッドを持つ 2 つのクラスを処理する必要がありますが、それらは同じインターフェイスを実装しておらず、同じスーパークラスを拡張していません。このクラスを変更することはできません/許可されておらず、このクラスのインスタンスを作成していません。このクラスのオブジェクトのみを取得します。多くのコードの重複を避ける最善の方法は何ですか?

クラスの 1 つ:

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

より良い解決策は、両方のクラスへの統一されたインターフェイスを表すインターフェイスを作成し、そのインターフェイスを実装する 2 つのクラス (1 つは A をラップし、もう 1 つは 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

ロギング フレームワーク 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/にあります。 helpers/ExceptionHelper.java .

アイデアは、例外ステートメントをより便利にする追加のコードを持つことができ、ロギング フレームワークごとにそれらを繰り返す必要がないということです。ラッパーは、コードの重複を排除する場所ではありません。コードの重複をなくすには、ラッパーと基礎となるクラスを呼び出す複数のバージョンのコードを記述する必要がありません。https://bradleyaross.wordpress.com/2016/05/05/java-logging-frameworks/を参照してください。

クラス bradleyross.helpers.GenericPrinter は、PrintStream、PrintWriter、および StringWriter の両方のクラスとインターフェイスで動作するコードを記述できる別のラッパーです。

于 2016-05-22T17:51:16.413 に答える
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 に答える