双方向の関連付けと逆更新を管理するための汎用Javaフレームワーク
-
05-07-2019 - |
質問
双方向の関連付けを処理する一般的な方法と、手動で記述されたJavaコードの逆更新を処理する方法を探していました。
私が何について話しているのかわからない人のために、ここに例を示します。以下は、(不満足な)解決策の私の現在の結果です。
public class A {
public B getB();
public void setB(B b);
}
public class B {
public List<A> getAs();
}
現在、関連付けのいずれかの端を更新する場合、一貫性を維持するために、もう一方の端も更新する必要があります。毎回手動で
a.setB(b);
b.getA().add(a);
または一致するコードをセッター/ゲッターに配置し、カスタムリスト実装を使用します。
依存関係が利用できなくなった、保守されていない古いプロジェクトを見つけました( https: //e-nspire-gemini.dev.java.net/ )。必要なコードを自動的に挿入するために使用される注釈を使用して、問題を処理します。
誰もがこれをジェミニの一般的で控えめな方法で扱う別のフレームワークを知っていますか?
ciao、 エルマー
解決
googleコレクション(googleの内部コードから)- http://code.google。 com / p / google-collections / はJavaジェネリックと互換性があります(互換性があるだけでなく、ジェネリックを非常によく使用します)
クラスBiMap- http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?http://google -collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/package-summary.html 双方向の関連付けが可能です。
これらのクラスの一部は、JDK 7に移行する予定です。
他のヒント
セッターを抽象化しない限り、何らかのイベント通知メカニズムを提供する必要があります。オブジェクトがJavaBeansの場合、PropertyChangeSupportの使用とプロパティ変更イベントの発生を検討しています。
それを行う(または変更を検出する他のメカニズムがある)場合、Glazed Listsは ObservableElementList を使用すると、リストの末尾から関連付けの同期を簡単に処理できます(つまり、AをList <!> lt; A <!> gt;に自動的に追加します) a.setB(b))を呼び出します。他の方向は、プロパティ変更の監視(または同等のもの)を使用して簡単に処理できます。
これは一般的なソリューションではないことは承知していますが、それは簡単な基盤になると思われます。
このようなものは、Bクラスの特別なリスト実装を必要にすることに注意してください-一般的なケースでそれを処理できるAOPタイプのソリューションに決して近づきません(つまり、ArrayListまたはそのようなものを使用します) )。
また、あなたが達成しようとしているのは、データバインディングの聖杯のようなものであることを指摘する必要があります。フィールドレベルでのバインディングには、適切な実装(ゲッターやセッターなど)があります(例については、JGoodiesバインディングおよびJSR 295を参照)。リストタイプバインディングには、本当に優れた実装が1つあります(上記の「グレーズドリスト」)。ほぼすべてのアプリケーションで、両方のテクニックを連携して使用していますが、あなたが求めているものと同じくらい抽象的なものにしようとしたことはありません。
これを設計している場合、次のようなものを見ます:
AssociationBuilder.createAssociation(A a, Connector< A> ca, B b, Connector< B> cb, Synchronizer< A,B> sync)
Connectorは、さまざまな変更通知タイプの単一のインターフェイスを可能にするインターフェイスです。シンクロナイザーは、一方のオブジェクトが変更されたときに両方のオブジェクトが同期するように呼び出されるインターフェイスです。
sync(ChangeInfo info, A a, B b) // make sure that b reflects current state of a and vice-versa.
ChangeInfoは、変更されたメンバーと、実際に変更された内容に関するデータを提供します。私たちです。この汎用性を本当に保とうとしている場合、フレームワークのユーザーにこの実装をパントする必要があります。
上記を導入すると、さまざまなバインド基準を満たす、事前定義された多数のコネクタおよびシンクロナイザを使用できるようになります。
興味深いことに、上記のメソッドシグネチャは、JSR 295 createAutoBinding()メソッド呼び出しに非常に似ています。プロパティオブジェクトは、Connectorと同等です。 JSR 295にはSynchronizerがありません(代わりに、ENUMとして指定されたバインディング戦略があります。さらに、JSR 295はproperty-<!> gt; propertyバインディングでのみ動作し、1つのオブジェクトのフィールド値をそのオブジェクトのリストにバインドしようとします別のオブジェクトのメンバーシップは、それらのテーブル上にもありません)。
意味をなすために、これらのクラスはピアになります。一貫性を保つために、パッケージプライベートメカニズム(友人の不在で)をお勧めします。
public final class A {
private B b;
public B getB() {
return b;
}
public void setB(final B b) {
if (b == this.b) {
// Important!!
return;
}
// Be a member of both Bs (hence check in getAs).
if (b != null) {
b.addA(this);
}
// Atomic commit to change.
this.b = b;
// Remove from old B.
if (this.b != null) {
this.b.removeA(this);
}
}
}
public final class B {
private final List<A> as;
/* pp */ void addA(A a) {
if (a == null) {
throw new NullPointerException();
}
// LinkedHashSet may be better under more demanding usage patterns.
if (!as.contains(a)) {
as.add(a);
}
}
/* pp */ void removeA(A a) {
if (a == null) {
throw new NullPointerException();
}
as.removeA(a);
}
public List<A> getAs() {
// Copy only those that really are associated with us.
List<A> copy = new ArrayList<A>(as.size());
for (A a : as) {
if (a.getB() == this) {
copy.add(a);
}
}
return Collection.unmodifiableList(copy);
}
}
(免責:テストもコンパイルもされていません。)
ほとんどの例外は安全です(例外の場合はリークする可能性があります)。スレッドセーフ、多数、パフォーマンス、ライブラリ化などは、興味のある読者の課題として残されています。
すべての提案をありがとう。しかし、私が探していたものに近いものはありませんでした。おそらく間違った方法で質問を作成しました。
geminiの代替品を探していたので、無限のチェックと特別なList実装でコードを汚染することなく、これを控えめな方法で処理する方法を探していました。もちろん、Kevinが示唆するように、AOPベースのアプローチが必要です。
もう少し調べてみると、cnet上にすべてのソースとソースとの依存関係を含むgeminiのパッケージが見つかりました。依存関係のソースが見つからないことが、私がそれを使用するのを妨げた唯一の懸念事項でした。現在、すべてのソースが利用可能であるため、バグを修正できます。 誰かがこれを探す場合: http://www.download.com/Gemini /3000-2413_4-10440077.html