10

私が今取り組んでいることの 1 つは、ゲームにいくつかの類似点があります。説明のために、架空の架空のゲームから引き出された例を使用して問題を説明します。

それをDeathBlaster 4: The Deatheningと呼びましょう。DB4 では、移動Ship中に定期的かつランダムに遭遇する多数のオブジェクトがありPhenomenaます。与えられた は、それに遭遇するPhenomenonに 0 個、1 個、または複数個ある場合があります。たとえば、4 種類のと 3 種類の があるとします。EffectsShipShipsPhenomena

                              現象
              ==========================================
船の重力もブラックホール星雲フィールド
------------ -------------------------------------------------- ----
RedShip +20% スピード -50% パワー -50% シールド
BlueShip 効果なし 無敵死 さまざまな効果
GreenShip -20% スピードデス +50% シールド 船の現象
イエローシップ死亡 +50% 威力 効果なし    

さらに、Effects相互に作用する可能性があります。たとえば、 aと a のGreenShip両方にある a は、生成されたとの間である種の相乗効果を引き出すことができます。そのような場合、相乗効果自体が、たとえば、この相互作用の結果である可能性があります。最終結果がどうあるべきかを解決するために、a に作用するセット以外の情報は必要ありません。GravityWellNebulaFieldSpeedEffectShieldEffectEffectPowerLevelSynergyEffectEffectsShip

ここで問題が発生する可能性があります。単純な最初のアプローチとして、 everyが every の処理方法を知っている必要があるか、everyが every について知っている必要がありますShipPhenomenonPhenomenonShip。これは明らかに容認できないため、これらの責任を別の場所に移したいと考えています。明らかに、ここには少なくとも 1 つの外部クラスがあり、おそらく aMediatorまたはVisitor何らかの種類です。

しかし、それを行うための最良の方法は何ですか? 理想的なソリューションには、おそらく次の特性があります。

  • 新しい を追加するのと同じくらい簡単に、新しいShipを追加できPhenomenonます。
  • 効果を生み出さない相互作用はデフォルトであり、表現するための追加のコードは必要ありません。設定より規約。
  • 相互にどのように相互作用するかを理解しEffects、これらの相互作用を管理して最終結果がどうなるかを決定することができます。

私は自分のアプローチがどうなるかをすでに決めていると思いますが、最良の設計のコンセンサスが何であるかを聞くことに興味があります. どこから始めますか?どのような道を探りますか?



フォローアップ更新:皆さん、ご回答ありがとうございます。これが私がやったことです。私の主な観察結果は、可能性のある×相互作用Effectsの数に比べて、異なる数が少ないように見えるということでした。つまり、相互作用の組み合わせはたくさんありますが、それらの相互作用の結果の種類の数は少なくなります。PhenomenaShips

たとえば、表には 12 の相互作用の組み合わせがありますが、効果は 5種類しかないことがわかります。速度の変更、パワーの変更、シールドの変更、無敵、死亡です。

InteractionResolver相互作用の結果を決定するために、3 番目のクラス を導入しました。Ship-Phenomenonペアをマップするディクショナリが含まれていますEffects(基本的には、エフェクトといくつかのメタデータを実行するデリゲートです)。相互作用の計算結果が完了すると、それぞれに対応するShipが渡されます。EffectStackEffects

Ships次に、 を使用して、既存の属性とプロパティに修飾子を追加することにより、それらEffectStackの の実際の結果を決定します。Effects

私はこれが好きです:

  • Shipについて知る必要はありませんPhenomena
  • Ship-関係の複雑さは、 にPhenomena抽象化されますInteractionResolver
  • 複数の、場合によっては複雑な効果を解決する方法の詳細は、 によって抽象化されていInteractionResolverます。船は必要に応じて効果を適用するだけです。
  • これにより、追加の便利なリファクタリングが可能になります。たとえば、船が効果を処理する方法EffectProcessorStrategyは、 . デフォルトはすべての効果を処理することかもしれませんが、たとえば、 aBossShipは異なる を持つことでマイナーな効果を無視するかもしれませんEffectProcessorStrategy
4

7 に答える 7

1

興味深い潜在的なオプションは、Visitor Patternのバリアントを使用することです。

Judith Bishop と R. Nigel Horspool は、デザイン パターンの効率性に関する論文を書き、C# 3 の機能を使用して従来のビジター パターンのさまざまなバリエーションを説明しました。

特に、訪問者パターンを処理するためにデリゲートとどのように連携するかを見ていきます。デリゲートのリストまたはスタックを使用すると、複数のオブジェクトから複数の効果を処理する興味深い方法が得られる可能性があり、クラス階層のいずれかの側を拡張する (船を追加するか、効果を追加する) ことが大幅にコードを壊す変更を加えることなく、はるかに簡単になります。

于 2009-03-26T00:14:25.493 に答える
0

問題の答えは、質問がどれだけうまくいくかにかかっていると思います。

設計の仕方は、質問が何であるか(または質問が将来進むかどうか)に依存すると思います

テーブルを指定すると、解決策はテーブルを維持してクエリを実行することだと思います。

ここにpythonコード:(テストされておらず、例として示しているだけです)

class Ship():
     def __init__(self,type):
         self.type=type
     def encounterPhenomena(self,type): # let Phenomena to process ship
         p = Phenomena(type)
         p.process(self)

class Phenomena():
     processTable = {}
     def __init__(self,type):
         self.type=type
     def process(self,ship):
         try:
             self.processTable[self.type](ship.type) #query the internal table to process ship
         except:
             pass #if Phenomena don't know this ship type then no process..
     def addType(type,processMethod):
         processTable[type]=processMethod #add new Phenomena, and add processMethod

def run():
    A = Ship(type = 'RedShip')
    A.encounterPhenomena(type='GravityWell');

プロセス メソッドが変更された場合は、Phenomena クラスのプロセス メソッドを変更するだけです。

ship が Phenomena の処理方法を知る必要があると思われる場合は、process メソッドを ship クラスに変更します。

または、フェノメナが船のステータスを変更する必要があるだけでなく(他の船、浅瀬の岩など)、船のクラスでプロセステーブルを維持し、フェノメナをその1つにする必要があると思います。

繰り返しになりますが、どのように設計するかは、質問自体に依存します。

于 2009-03-27T03:34:17.923 に答える
0

興味深い質問

何らかの方法で、船は、どの現象が自分に影響を与える可能性があるか、どの現象がどの船に影響を与えるかを知る必要があります。

これは、実行時に解析される xml ファイルに格納できます。

Decorator パターンを使用して効果を計算できるかもしれません。実行時にさまざまな現象を生成します。

あなたの船が Interface IShip を実装していて、コードのあらゆる場所で IShips を使用しているとします。

ここで、すべてのフェノメナが Interface IShip (デコレータ デザイン パターンで必要) も実装しているとします。

IShip myShip = myShip.AddPhenomena(PhenomenaGeneratedByAFactoryForThisShip);

フェノメナでは、元のシップのメソッドをラップするので、プロパティなどすべてを変更できます。

また、戦略パターンを使えば、どんな現象でも発生させることができます。

フェノメナの解除は手持ちの装飾船の山を歩き回ってラッピングすることで使えるので問題ないと思います。

相乗効果に関しては、少し修正した現象を使用すると思います。これは、対象の船がそれ自体にすべての現象を持っている場合にのみ機能します。

于 2009-03-26T00:22:59.013 に答える
0

完全な答えではありませんが、私にとってこれはかなり「プロパティパターン」を叫んでいます。これについては有名な yegge の暴言があります。

http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

于 2009-03-26T00:05:12.160 に答える
0

これは、古典的な OOP ポリモーフィズム/ビジター ジレンマのように見えます。ただし、要件によっては簡単になります。

Ship基本的に、すべての具体的な Ship の派生元となる基本クラスを作成します。このクラスには次のメソッドがあります。

class Ship
{
  void encounterBlackHole() {}
  void encounterNebula() {}
  ... etc. ...
};

空のデフォルト ボディを使用します。新しい Phenomenon を追加するときは、本体が空の新しいメソッドを追加するだけです。(メソッドには、座標やブラック ホールの重みなどの引数が含まれる場合があります。)

エフェクトとその相互作用については、たとえば、どのようにしたいのかについて、さらに情報を追加する必要があると思います。相互作用はまれか通常か、それらは何らかの累積的なものか、それらが制御する変数の限られたセットに限定されているか...

于 2009-03-26T00:12:23.207 に答える
0

私には古典的な複数のディスパッチの問題のように思えます。

于 2009-03-26T00:20:02.093 に答える
0

すべての船がすべての現象を処理する方法を知っている必要があるか、すべての現象がすべての船について知っている必要があります。

これがあなたの問題の鍵だと思います。すべての船と現象の相互作用が一意である場合、それは真実です. あなたが作成したテーブルでは、そのように見えるので、n 個の船と m 個の現象の場合、n*m の相互作用を指定する必要があります。メンテナンスの問題を嗅ぐのは正しいです。

唯一の解決策は、現象の影響を船の特性に依存させることによって、船と現象の相互作用を独特なものにしないことです。たとえば、ストレンジ アルミナで建造された船だけがブラック ホールに耐えられると言えます。

しかし、プロパティが 1 つだけでは何も得られません。ブラック ホールの影響を受ける船を簡単に特定できたはずです。重要なのは、複数のプロパティが同じ組み合わせの爆発を示すことです。

そのため、ストレンジ アルミナで建造された船はブラック ホールに耐え、それなしで建造された船はより速く進むことができます。1 つのプロパティで 2 つのことを指定できます (1 ビット = 2 つの可能性)。コーバイト エンジンを搭載した船は、ワープ ゾーンでより速く移動します。コルバイト エンジンとストレンジ アルミナの両方を搭載した船は、星雲フィールドなどで 50% のシールドを獲得します。

シップにプロパティを追加すると、すべてのフェノメナがすべてのシップとどのように相互作用するかを指定する必要がなくなり、それでもすべてのフェノメナとすべてのシップが適切な動作を示すようになります。

M 隻の船がある場合、すべての船に固有の動作を与えるには、log2(M) プロパティのみが必要です。

于 2009-03-26T00:41:55.303 に答える