1

私は3種類のオブジェクトを持っています:

  • コンポーネント: パッケージと名前で識別
  • モデル: パッケージと名前で識別
  • 機能: 名前で識別

関係は次のように定義されます。

  • コンポーネントは 0 個以上のモデルを持つことができます [0..*]
  • モデルは 0 個以上の関数を持つことができます [0..*]
  • 関数は 1 つまたは複数のモデルに含めることができます [1..*]
  • モデルは 0 個以上のコンポーネントに含めることができます [0..*]

自然な設計は、コンポーネントにモデルへの一連の参照を持たせ、モデルに関数への一連の参照を持たせることです。この設計により、トップダウン方式でリレーションシップを簡単にナビゲートできます (また、「このモデルにはどのような関数が含まれていますか?」などのクエリに簡単に答えることができます)。

問題は、もっと柔軟性が必要だということです。これらの種類のクエリに答えるために簡単にナビゲートできるものを用意したいと思います:

  1. 関数名を指定すると、この関数はどのモデルで、どのコンポーネントで参照されますか?
  2. モデルのパッケージと名前を指定すると、そのモデルはどのコンポーネントで参照されますか?
  3. どのコンポーネントからも参照されていないモデルはありますか?

HashMap<Component, Model>Component、Model、および Function を単純な POJO として使用し、それらの間の参照を複数の HashMap ( 、Hashmap<Model, Component>、 )HashMap<Model, Function>で追跡することを考えましHashMap<Function, Model>たが、これは私には非効率的です。

より良いデザインのものを提案してもらえますか?

4

3 に答える 3

2

問題のドメインを表す論理データ構造はグラフですが、文字通りそうしても、例として挙げたクエリに答える「効率的な」手段は得られません。これらのクエリが、より大きなセットであると想像するものから考え出した単なる例なのか、それとも要件の完全な仕様を構成しているのかを知ることは役に立ちます。

この回答は気に入らないと思いますが、ここで最もメリットがあると思われるのはリレーショナル データベースです。そのようなデータベースを使用する際の通常の複雑さを回避したい場合は、すべてのデータをメモリに保持するものを埋め込むことができます。SQLite は考慮すべきリレーショナル データベースの 1 つですが、Java で使用できるデータベースは他にも多数あります。

あなたの言い回しに基づいて、私はその結論に達します。グラフのエッジ (またはエンティティ間の集約関係) を両方向にナビゲートすることについて言及しています。問題のリレーショナル モデルで表現するのは簡単ですが、提案するマップのようなメモリ内構造を使用すると非常に難しくなります。前者には関係間の外部参照への暗黙の方向性がありませんが、後者はあるエンティティから別のエンティティへの一方向参照のみを表すことができます。

リレーショナル モデルでは、事実を次のように表現できます。

  • これらのプロパティを持つComponentsと呼ばれるエンティティがあります。
  • これらの他のプロパティを持つModelsと呼ばれるエンティティがあります。
  • ComponentsModelsの間にはリンクがあり、すべてのModelは任意の数のComponentsから「到達可能」であり、すべてのComponentは任意の数のModelsに「到達可能」です。(「含まれる」または「所有する」または排他性を示唆するその他の関係については書いていないことに注意してください。)

リレーショナル モデルを使用すると、リンクがどちらの方向を指しているかに偏りなく、これらのリレーションに対してクエリを評価できます。もちろん、リンクは何らかの意味を持つアサーションです。おそらく方向性があり、論理グラフを無向グラフではなく有向グラフにします。問題のドメインに固有のものですが、その意味を理解するのはアプリケーションであり、データベースやリレーショナル モデルが管理するものではありません。その操作。

論理モデルを超えて、クエリにできるだけ効率的に答えるには、データベースが制約に基づかないインデックスを維持するように指定する必要があります。一部のレコードは、整合性制約以外に特別なことを要求しなくても効率的に検索できます。これは、データベースが独自にインデックスを構築して、記述された制約を効率的に実施するのに役立つ可能性があるためです。しかし、特定のComponentModelの間に既にペアリングが存在するかどうかはすぐにわかるかもしれませんが、特定のModelを参照しているComponentsがあるとしても、それには答えられません。

データベースにそのようなインデックスを維持するように要求することは、最初に提案したメモリ内マップの一部を維持することに似ていますが、設計アプローチには違いがあります。設計では、出現する可能性のあるクエリの一部に答えられません。関係はナビゲートできる方法でキャプチャされないため、たとえ非効率的であってもですただし、データベースの場合、新しいクエリへの適応は通常、既存のデータに対するクエリを高速化するために追加のインデックスを定義することです。. 言い換えれば、データベースはまだ初期のクエリに答えることができます。そうするために恥ずかしい方法で苦労しなければならないかもしれません。適切なインデックスを定義するまでは、期待した他のクエリと同じくらい効率的にこれらのクエリを処理する準備が整います。

ここでもう 1 点指摘します。ここでリレーショナル データベースを使用するのは、技術的にはやり過ぎかもしれませんが、正しい教訓的な選択です。それはあなたの問題に値する解決策です。データベースの機能の小さなサブセットを提供し、ニーズを満たす、より狭く、より調整されたものを構築できますが、そうすることで、ここでのより大きな設計上の教訓を見逃していると思います. データベースを使用して問題をモデル化する方法を学習するのではなく、問題を使用して、データベースの実装方法について学習することになります。後者を可能かつ簡単にすることが、業界がそのようなデータベース技術を利用できるようにした理由です。

于 2012-05-28T15:12:34.293 に答える
1

単一のコレクションで記述したすべての関係を追跡できるようにするデータ構造はですMultiMapJavaチュートリアルのマップインターフェイスセクションにJavaマルチマップの説明があります(マルチマップセクションまでスクロールするか、リンクをたどってマルチマップのページを検索する必要があります。チュートリアルのそのセクションへの直接のアンカーはありません) 。MultiMapJava用の利用可能な実装があります:

を使用してMultiMap、他のオブジェクトを含むまたは集約できるオブジェクトタイプのマッピングを作成できます。

//Associate multiple Models to one Component:
multiMap.put( componentD, modelN );
multiMap.put( componentD, modelO );
multiMap.put( componentD, modelP );
//Associate multiple Models to a Component (some different, some the same)
multiMap.put( componentE, modelQ );
multiMap.put( componentE, modelR );
multiMap.put( componentE, modelN ); //also associated with componentD
//And associate multiple Functions to one Model:
multiMap.put( modelQ, functionG );
multiMap.put( modelQ, functionH );
multiMap.put( modelQ, functionI );

Collectionマップされたキーに関連付けられているを後で取得できます。MultiHashMap次に、Apache CommonsCollectionsapi-docを使用した例を示します。

Collection modelFunctions = multiMap.get( modelQ );

Componentこのアプローチにより、トップダウンでからへの移動が容易にModelなりFunctionます。ただし、各関係の両端をに追加すると、ボトムアップトラバーサルを簡単に実行できるようにすることもできますMultiMap。たとえば、aModelとaの関係を確立するには、次のFunctionようにします。

multiMap.put( modelR, functionJ );
multiMap.put( functionJ, modelR );

両方の関係がマップされているため、 (上記の例のように)にFunction含まれるすべてのを簡単に取得することも、 :を含むすべてのを簡単に取得することもできます。ModelModelFunction

Collection functionModels = multiMap.get( functionJ );

もちろん、これは、関係を壊したい場合は、MultiMapから両方のマッピングを削除することを忘れないでくださいが、それはかなり簡単であることも意味します。これがお役に立てば幸いです-

于 2012-05-28T16:29:29.650 に答える
1

もう1つのオプションは、作成クエリから抽象化することです。これにより、パフォーマンス/使いやすさの点でボトルネックになったときに、特定の実装を簡単に最適化/拡張できる可能性があります。

このようなインターフェースは次のようになります。

public interface IEntityManager {

    // methods for retrieving entities by plain queries

    Component getComponent(String package, String componentName);

    Model getModel(String package, String modelName);

    Function getFunction(String name);

    // more specific queries

    List<Component> getComponents(Function function);

    List<Model> getModels(Function function);

    // even more specific queries ...
}

このようにして、任意の実装に必要なレベルのパフォーマンスを提供しながら、本番コードでこのインターフェイスを使用できます。

Componentさて、具体的な実装に関しては、すべて、インスタンス、およびそれらの関係に応じてわずかな違いがModelあります。Function

  1. アプリケーションの開始時デスクトップアプリのメソッドの開始時Webアプリのメソッドの開始時など)に作成され、アプリケーションの実行中に変更されないmainServletContextListener.contextInitialised
  2. アプリケーションの実行中に作成/削除/更新されます

単純なので、最初のケースから始めましょう。すべてComponentのインスタンス(およびそれらの間の関係)が、これらのエンティティを使用してロジック間で共有されるインスタンスに認識されていることを確認する必要がModelあります。これを実現する最も簡単な方法の1つは、エンティティクラスを実装と同じJavaパッケージに入れ、コンストラクタをパッケージプライベートにして、作成ロジックを具体的な実装に移行することです。FunctionIEntityManagerIEntityManagerIEntityManager

package com.company.entities;

public class Component {
    public final String package;
    public final String name;

    Component(String package, String name) {
        this.package = package;
        this.name = name;
    }

    // ...
}

// similar Model and Function class declarations

public class EntityManager implements IEntityManager {

    private final Map<Pair<String, String>, Component> components = new HashMap<Pair<String, String>, Component>();
    private final Map<Pair<String, String>, Model> models = new HashMap<Pair<String, String>, Model>();
    private final Map<String, Function> functions = new HashMap<String, Function>();

    // variation of factory-method
    public Component addComponent(String package, String name) {
        // only this EntityManager can create instances
        // so one can be sure all instances are tracked by it
        final Component newComponent = new Component(package, name);
        components.put(new Pair(package, name), newComponent);
    }

    // ... addModel, addFunction methods

    public void addFunctionToModel(Function function, Model model) {
        // here one should store 'somehow' information that function is related to model
    }

    public void addModelToComponent(Model model, Component component) {
        // here one should store 'somehow' information that model is related to component
    }

    // ... other methods
}

最初のケース(すべてのエンティティはアプリケーションの開始時に作成されます)では、Builderパターンを使用してEntityManagerクラスのインスタンスを作成することもできます。これにより、作成ロジックが使用法から明らかに抽象化されます。ただし、2番目のケースでは、クラスに、のようなメソッドが必要addComponentですaddModelToComponent(1つのインスタンスのシングルインスタンスをマルチスレッドで使用する場合はEntityManager、メソッドで状態をスレッドセーフに変更することを検討する必要があります)。

そして最後に、エンティティ間の関係をどのように正確に保存する必要があるかについてです。そのような関係を持つエンティティを効率的に保存/取得するための特効薬はありません。関係がそれほど多くないエンティティが1000を超えない場合、検索はHashMap<String, Function>非常に高速であり、ボトルネックになることはありません。そして、それがボトルネックになった場合、どの種類のクエリがより頻繁に使用され、どの種類のクエリがめったに使用されないかを徹底的に調べ、その観察に基づいてEntityManager内部実装を調整する必要がありIEntityManagerます。

アプリケーション内のインスタンスに関してはEntityManager、明らかに、インスタンスは1つだけである必要があります(非常に特殊な場合を除く)。これは、Singletonパターン(あまり好ましくないソリューションですが、しばらくは正常に機能する可能性があります)を使用するか、アプリケーションEntityManager開始時にインスタンス化して、明示的なインスタンスをそれを必要とするクラス/メソッドに渡すことで実現できます(より好ましいソリューション)。

お役に立てれば ...

于 2012-05-29T00:34:45.593 に答える