私は(いくつかのテンプレートから生成された)しっかりと関連するクラスを提供する外部ライブラリを使用して、残念ながら共有インターフェース、例えばなしです
public class A {
public UUID id();
public Long version();
public String foo();
public String bar();
}
public class B {
public UUID id();
public Long version();
public String foo();
public String bar();
}
public class C {
public UUID id();
public Long version();
public String foo();
public String bar();
}
// ... and more: D, E, F, etc.
私は外部のライブラリに対して何ら影響を与えない考えると、同じメソッドシグネチャを共有するクラスのグループへの書き込みロジック共通の慣用的な方法は、(少なくとも、メソッドのための共通ロジックによって使用されている)何ですか?
現在、私はケースバイケースで、3つのいずれかの操作を行います。
私は、それぞれのオブジェクトから原始的な結果を取るヘルパーメソッドを書く例:
private static void myHelper(UUID id, Long version, String foo, String bar) { ... }
私は関係なく、その型のオブジェクト「アンパック」することができますこの方法:
myHelper(whatever.id(), whatever.version(), whatever.foo(), whatever.bar());
しかし、それは私が多くのメンバーで作業する必要がある場合は特に、非常に長ったらしい得ることができます。
私は(ゲッターで働いているシナリオではすなわちアクセスのみにオブジェクトの現在の値を必要とする)、私は私自身の共通のクラスにAまたはBまたはCをマッピングするためのドーザーやModelMapperなどの使用マッピングライブラリへの道を見つけました、例えば
public class CommonABC { UUID id; Long version; String foo; String bar; }
構成で弾くと、これらのライブラリは、あなたのクラスに、メソッドやフィールド、パブリックまたはプライベートかどうか、すべてのメンバーをマッピングするために取得することができます例えば
modelMapper.getConfiguration() .setFieldMatchingEnabled(true) .setFieldAccessLevel(Configuration.AccessLevel.PRIVATE);
しかし、これは「段平」アプローチのようなものだった、IMO、明らかではないハックは、重複したコードからの要因に過ぎない正当化。
最後に、その他の特定のシナリオでは、単に行うことが最も簡潔でした
private static void myHelper(Object extLibEntity) { if (extLibEntity instanceof A) { ... } else if (extLibEntity instanceof B) { ... } else if (extLibEntity instanceof C) { ... } else { throw new RuntimeException(...); } }
これが悪い理由は明らかです。
あなたはこの方法でライブラリと一緒に暮らす必要があり、企業の状況で、あなたは何をしますか?
冗長な場合、私は、非常に明示的に書くに傾いています、最初からこれらのエンティティを変換するマッパー(ジェネリックマッパーライブラリを使用していません)。しかし、私はより良い方法があるかどうだろうか。(同様に、「キャスト」への道は、実行時に、新しいインターフェイスを実装するように、オブジェクトがありますか?)
(フードの下)第二のアプローチにそう似ていますが、比較的リーンと柔軟性があるオプションは、使用することですダイナミックプロキシクラスを。わずか数行のコードを使用すると、任意のオブジェクトがある限り、それは必要なメソッドを持っているとして、特定のインターフェイスを実装するために「見える」させることができます。以下はMCVEであることを示しているという基本的なアプローチ:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
public class DelegatingProxyExample {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
CommonInterface commonA = wrap(a);
CommonInterface commonB = wrap(b);
CommonInterface commonC = wrap(c);
use(commonA);
use(commonB);
use(commonC);
}
private static void use(CommonInterface commonInterface) {
System.out.println(commonInterface.id());
System.out.println(commonInterface.version());
System.out.println(commonInterface.foo());
System.out.println(commonInterface.bar());
}
private static CommonInterface wrap(Object object) {
CommonInterface commonInterface = (CommonInterface) Proxy.newProxyInstance(
CommonInterface.class.getClassLoader(),
new Class[] { CommonInterface.class }, new Delegator(object));
return commonInterface;
}
}
// Partially based on the example from
// https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
class Delegator implements InvocationHandler {
private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;
static {
try {
hashCodeMethod = Object.class.getMethod("hashCode", (Class<?>[]) null);
equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class });
toStringMethod = Object.class.getMethod("toString", (Class<?>[]) null);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
private Object delegate;
public Delegator(Object delegate) {
this.delegate = delegate;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Class<?> declaringClass = m.getDeclaringClass();
if (declaringClass == Object.class) {
if (m.equals(hashCodeMethod)) {
return proxyHashCode(proxy);
} else if (m.equals(equalsMethod)) {
return proxyEquals(proxy, args[0]);
} else if (m.equals(toStringMethod)) {
return proxyToString(proxy);
} else {
throw new InternalError("unexpected Object method dispatched: " + m);
}
} else {
// TODO Here, the magic happens. Add some sensible error checks here!
Method delegateMethod = delegate.getClass().getDeclaredMethod(
m.getName(), m.getParameterTypes());
return delegateMethod.invoke(delegate, args);
}
}
protected Integer proxyHashCode(Object proxy) {
return new Integer(System.identityHashCode(proxy));
}
protected Boolean proxyEquals(Object proxy, Object other) {
return (proxy == other ? Boolean.TRUE : Boolean.FALSE);
}
protected String proxyToString(Object proxy) {
return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
}
}
interface CommonInterface {
UUID id();
Long version();
String foo();
String bar();
}
class A {
public UUID id() {
return UUID.randomUUID();
}
public Long version() {
return 1L;
}
public String foo() {
return "fooA";
}
public String bar() {
return "barA";
}
}
class B {
public UUID id() {
return UUID.randomUUID();
}
public Long version() {
return 2L;
}
public String foo() {
return "fooB";
}
public String bar() {
return "barB";
}
}
class C {
public UUID id() {
return UUID.randomUUID();
}
public Long version() {
return 3L;
}
public String foo() {
return "fooC";
}
public String bar() {
return "barC";
}
}
もちろん、内部で、この用途の反射、あなたは何をやっている知っている場合にのみ使用する必要があります。特に、あなたがマークされている場所で、いくつかの賢明なエラーチェックを追加する必要TODO
があり、インタフェースのメソッドは、指定したデリゲートオブジェクト内で検索されています。