5

小さなシード グラフから大きな Provenance グラフを生成できるアプリケーションを Java で作成しようとしていますが、クラスを設計する最善の方法を見つけるのに少し苦労しています。

まず、Provenance には基本的にグラフ構造、ノード、およびエッジがあります。Provenance Data Model から Java オブジェクトへのマッピングとして機能する Java ライブラリを作成しました。これにより、Provenance モデルからアプリケーション固有の情報を抽象化できます。

私のクラス構造は次のようになります。

  • グラフ (ノードとエッジのセットを含む)
  • 抽象ノード (今のところ文字列名のみ)
    • エージェント
    • アクティビティ
    • 実在物
    • Node のその他のサブクラス
  • 抽象的なエッジ
    • 世代
    • 協会
    • Edge のその他のサブクラス

今、私がやりたいのは、乗数/飽和レベルとして機能するノードとエッジに重みを付けることです。ライブラリを使用してこの目的を達成する方法はいくつかありますが、開発と保守性の観点から何が最適かはわかりません。

最初に、最小および最大の重みの取得と設定など、いくつかの単純なメソッドを使用して Weighable インターフェイスを定義しました。

ここで、各サブクラス Node を拡張することができます。

class WeighableAgent extends Agent implements Weighable

しかし、これには利用可能なすべてのタイプのノードの拡張クラスが必要であり、すべてのレベルで Weighable インターフェースを実装する必要がありました。

別の方法として、ミックスインを提供することもできますが、それでも各サブクラスで同じ機能を実装する必要があります。

別の方法として、Agent に基づいて構成するクラスを作成することもできますが、それでもすべての構成クラスに Weighable を実装する必要があります。

または、Node クラスだけで構成することもできます。

class WeighableNode implements Weighable {

    private Node node;

    public WeighableNode(Node node) {
        this.node = node;
    }

    etc etc...

これにより、Weighable を 1 か所にしか実装できなくなります。ただし、その後、WeighableNode を使用して何かから具体的なクラス型に関する重要な情報を失います。これを回避する唯一の方法は、メソッドを提供することです。

Node getNode();

デメテルの法則で気になるところ。さらに、この WeighableNode は PresentationNode によって構成され、Swing を支援します。これは、次のような呼び出しを連鎖させることになることを意味します。

presentationNode.getWeighableNode().getNode() instanceof Agent

これは非常に不快に思えます。

私が考えた最終的な解決策の 1 つは、上記のように Node で構成し、WeighableNode を WeighableAgent などで拡張することです。これは、毎回 Weighable を再実装する必要がないことを意味します。

instanceof WeighableAgent

ラップされたノードはエージェントです。

これが非常に長いことを理解していますが、経験豊富な開発者が正しい設計手法をすぐに理解できることを願っています。

4

1 に答える 1

2

残念なことに、Java には実際の mixin も多重継承もありません…</p>

汎用ラッパー

一般的なラッパータイプWeighableNode<NodeType extends Node>として使用すると思います。そうすれば、特定のタイプのノードを必要とするすべての場所で、その事実を明確に述べることができます。そのメソッドは正しいクラスを返す可能性があります。また、あちこちにクラスが多すぎることもありません。getNode

変換イディオム

上記のアプローチを使用しない場合でも、次のイディオムは興味深いものになる可能性があります。

class Node {
    public T asNode(Class<T extends Node> clazz) {
        return clazz.cast(this);
    }
}

class NodeWrapper<N extends Node> {
    private N realNode;
    public T asNode(Class<T extends Node> clazz) {
        try {
            return super.asNode(clazz);
        }
        catch (ClassCastException e) {
            return realNode.asNode(clazz);
        }
    }
}

null必要に応じて、例外をスローする代わりに上記を返すように変更できます。アイデアは、呼び出し元のコードがノードをさまざまな型に変換する方法を心配する必要がないということです。たとえば、単に次のように記述します。

presentationNode.getNode(Agent.class)

これにより、実装がインターフェイスから分離され、必要が生じた場合に後で変更する自由が大きくなります。基本的なNode実装は、派生クラスを処理して、それ自体を変換する方法を知っています。NodeWrapper合成を使用して変換を追加します。それを と の基本クラスとして使用できWeighableNodeますpresentationNode

ファット インターフェイス

計量機能Node自体を実装しWeighedNodeて、タグ付けインターフェイスとして使用することも、まったく使用しないこともできます。最も良い解決策ではありませんが、クラスの数を抑えるのに役立ち、余分なデータによって消費されるメモリのビットを除いて、実際に害を及ぼすべきではありません。他のスキームで必要とされる間接化のレベルは、おそらくそのメモリ要件をはるかに上回るでしょう。

于 2012-07-18T15:59:44.053 に答える