「ブローカー定義セット」設計パターン — 別の名前でよく知られていますか?

StackOverflow https://stackoverflow.com/questions/401720

質問

私が長年関わってきたプロジェクトで、私にとって非常に役立つことが証明されたデザイン パターンを徐々に進化させてきました。時々、少し福音主義的になるべきだと思うこともありますが、試してみて、それが誰かの古い帽子の私バージョンに過ぎないことがわかったら、少し恥ずかしいでしょう。掘り下げてきました デザインパターン 探しても無駄だし、それについて話している人にも出会っていないが、私の検索は網羅的ではなかった。

中心的なアイデアは、定義オブジェクトのセットを管理するブローカー オブジェクトを持ち、各定義オブジェクトが何らかの複雑なプロパティの可能な値を構成することです。たとえば、すべて EngineType を持つ Car、Plane、および Generator クラスがあるとします。Car は独自の EngineType オブジェクトを保存せず、そのエンジンの種類を示すある種の参照キー (整数や文字列 ID など) を保存します。EngineType、たとえば WankelEngine のプロパティや動作を調べたいときは、EngineTypeBroker シングルトン オブジェクトに WankelEngine の定義オブジェクトを要求し、参照キーを渡します。このオブジェクトは、EngineTypes について知っておくべき興味深い内容をすべてカプセル化します。おそらく単なるプロパティ リストですが、潜在的に動作もロードされます。

したがって、それが促進しているのは、一種の共有された疎結合集合体であり、多くの Car が WankelEngine を持っている可能性がありますが、WankelEngine 定義オブジェクトは 1 つだけです (そして、EngineTypeBroker はそのオブジェクトを置き換えることができ、疎結合を強化されたランタイムモーフィズムに活用します)。

私が使用しているこのパターンのいくつかの要素 (引き続き EngineType を例として使用します):

  1. 指定された値が EngineType の有効な参照キーであるかどうかを判断するため、および参照キーに対応する EngineType 定義オブジェクトを取得するために、それぞれ IsEngineType(x) 関数と EngineType(x) 関数が常に存在します。
  2. 私は常に、特定の EngineType に対して複数の形式の参照キー、少なくとも文字列名と定義オブジェクト自体、多くの場合は整数 ID、そして場合によっては EngineType を集約するオブジェクト タイプを許可します。これはデバッグに役立ち、コードがより柔軟になり、私の特定の状況では、古い慣行と比較して多くの下位互換性の問題が軽減されます。(このプロジェクトのコンテキストでこれらすべてを行うために使用されていた通常の方法は、EngineType が持つ可能性のある各プロパティのハッシュを定義し、参照キーによってプロパティを検索することでした。)
  3. 通常、各定義インスタンスは、その定義タイプの一般クラスのサブクラスです (つまり、WankelEngine は EngineType を継承します)。定義オブジェクトのクラス ファイルは、/Def/EngineType などのディレクトリに保存されます (つまり、WankelEngine のクラスは /Def/EngineType/WankelEngine になります)。そのため、関連する定義がグループ化され、クラス ファイルは EngineType の構成ファイルに似ていますが、コードを定義する機能があります (通常、構成ファイルには含まれません)。

簡単に説明するサンプル疑似コード:

class Car {

    attribute Name;
    attribute EngineTypeCode;

    object GetEngineTypeDef() {
        return EngineTypeBroker->EngineType(this->GetEngineTypeCode());
    }

    string GetDescription() {
        object def = this->GetEngineTypeDef();
        return "I am a car called " . this->GetName() . ", whose " .
            def->GetEngineTypeName() . " engine can run at " .
            def->GetEngineTypeMaxRPM() . " RPM!";
    }

}

それで、これに名前はありますか?

役に立ちましたか?

解決

シングルトンレジストリ

信じられないかもしれませんが。今朝、私も全く同じことを考えていました。

私は以前にこのパターンを使用したことがありますが、そのリファレンスを見つけたことも、名前の付け方も知りませんでした。

これは一種の「キー付き」シングルトンだと思います。インスタンスはどこかに保存され、キーを使用して取得されます。

最後にこれを使用したのは、さまざまなソースからデータを取得するためでした。

約50のデータベーステーブルがありました(10にします)そして、データが表示されるフロントエンド「テーブル」がありますが、データはこれらのソースのいずれかから取得される可能性があり、それぞれに異なるロジック(クエリ、結合、キー)が必要です、など。)

このフロントエンドは「構成可能」だったので、どの値が表示され、どの値が表示されないかを知ることができませんでした。

解決策は、columnName (フロントエンド) をキーとして取得し、正しいインスタンスを取得して適切なクエリを作成することでした。

これは最初にハッシュ マップにインストールされ、後でデータベース テーブルから取得されました。

コードは次のようなものでした:

class DataFetcher {
    abstract Object getData( Object id );
}

class CustomerNameDataFetcher extends DataFetcher {
    Object getData( Object customerId ) { 
        // select name from customer where id = ? 
     }
}

class CompanyAdressDataFetcher extends DataFetcher { 
     Object getData( Object customerId ) { // don't ask why.
          // select name from company , customer where customer.co = company.co and cu = ?  etc.
     }
} 

class ProductColor extends DataFetcher { 
     Object getData( Object x ) { 
     // join from customer to color, to company to season to a bunch of table where id = ? 
}

// And the list goes on.

各サブクラスは異なるロジックを使用しました。

実行時にユーザーはビューを構成し、見たいものを選択します。

ユーザーが表示する列を選択したとき、私は列名と ID を使用してデータをフェッチしました。

DataFetchers はすべて、クラス メソッド内の親クラス (これに別のクラスを用意したくありませんでした) にインストールされました。

class DataFetcher {
    abstract Object getData( Object id );

    private static final Map fetchers = new HashMap();static { 
        fetchers.put("customer.name", new CustomerNameDataFetcher() );
        fetchers.put("company.address", new CompanyAdressDataFetcher () );
        fetchers.put("product.color", new ProductColor () );
        ...
    }
    public static DataFetcher getFetcher( String id ) { 
        return fetchers.get( id );
    }      

}

最後にフロントエンドテーブルを埋めるために、次のように呼び出します。

疑似コード

 for each row in table 
      for each column in row
          column.text = DataFetcher.getFetcher( column.id ).getData( row.id )
       end
 end

こんな感じでしょうか?それとも私があなたの説明を読み間違えたのでしょうか、私の説明はまったく異なります。

最後に、これは SingletonRegistry かそのような名前だと思います。私は(おそらく)あなたのことが好きで、必要に迫られてこれを作成しました。おそらくこれが一般的なパターンです。

他のヒント

私はこれに似たパターンを以前、ゲームでよく使用したことがあります。WeaponDefinition クラスと WeaponInstance クラスを用意します (これらの名前は適切ではありません)。WeaponDefinition クラス (およびさまざまな種類の武器がある場合はさまざまなサブクラス)近接対発射体など)は、その種類の武器のグローバル データ(発射速度、最大弾薬、名前など)を追跡する責任があり、すべてのロジックを備えています。WeaponInstance クラス (およびサブクラス) には、発砲シーケンスの現在の状態 (発射速度の比較に使用するため)、現在の弾薬数、ポインター (例のようにマネージャー クラスへのキーになる可能性がありますが、それはパターンの要件ではないようです)を WeaponDefinition に追加します。WeaponInstance には、発射、リロードなどのための一連の関数があり、それらは WeaponDefinition インスタンスの適切なメソッドを呼び出し、それ自体を引数として渡すだけです。これは、WeaponDefinition の内容がゲーム世界のすべての戦車/兵士/飛行機で重複しているわけではなく、すべて独自の弾薬数などを持っていることを意味します。

それが何と呼ばれているのか分かりませんし、あなたが話していることとまったく同じであるかどうかもわかりませんが、それに近いものだと思います。間違いなく便利です。

私にとっては、GoF Builder、Prototype、そしておそらく Featherweight を組み合わせたもののように思えます。

それはフライウェイトの一種のように聞こえます(多くの車がワンケルエンジンを共有しています)。しかし、それはどのように意味があるのでしょうか?ほとんどの車にはエンジンが搭載されていますが、同じエンジンのインスタンスを搭載できる車が何台あるでしょうか?彼らはそこまで遠くには行けないだろう。それとも、多くの車が WankelEngine タイプのエンジンを搭載しているということでしょうか?そのほうが理にかなっています。では、「WankelEngine 定義オブジェクト」は何に役立つのでしょうか?そのオブジェクトのフレーバーを構築し、それを要求者に返すのはファクトリーですか?そうであれば、それは定義オブジェクトのようには聞こえません。むしろ、構築するオブジェクトのパラメーターを取得してそのオブジェクトを返すファクトリーのように聞こえます。

ここには GoF の優れたプラクティスがいくつかあります。特に、継承ではなく合成していることがわかります (私の車にはエンジンとエンジンがあります)私の車のエンジンは WankelEngine です)。この引用を正確に思い出せればいいのですが、「継承はカプセル化を破る」とか「継承より合成を優先する」といったようなものです。

これによってどんな問題が解決されるのか興味があります。かなりの複雑さが追加されていると思いますが、私にはそのような複雑さの必要性がわかりません。もしかしたら、あなたの言語に特有の何かが私には理解できないのかもしれません。

GoF 担当者は、パターンをより大きなパターンに合成することについて議論しています。特に MVC は他の 3 つのパターンの集合体です。みたいなことをやったみたいですね。

これは、フェザーウェイトがシングルトンとして登録される Service Locator に少し似ています。

あなたが持っているのは地図、別名辞書です。複数のキーを 1 つのオブジェクトにマップすることで、ユニークなひねりを加えることができます。

なぜ地図なのか:

  • 鍵を保管しておく
  • キーを使用して、何らかのデータ構造から値をクエリします。

実装は次の場所にあります。

  • STL (以下を含む)地図)
  • Java (インポート:java.util.Dictionary)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top