テンプレート方式と戦略の組み合わせ


14

私のソフトウェアエンジニアリングクラスの課題は、特定のゲームでさまざまな形式でプレイできるアプリケーションを設計することです。問題のゲームはMancalaです。これらのゲームのいくつかはWariまたはKalahと呼ばれます。これらのゲームはいくつかの面で異なりますが、私の質問では、ゲームが以下の点で異なる可能性があることを知っておくことが重要です。

  • 移動の結果が処理される方法
  • ゲームの終了が決定される方法
  • 勝者が決定される方法

これを設計するために最初に思いついたのは、戦略パターンを使用することでした。アルゴリズムのバリエーション(ゲームの実際のルール)があります。デザインは次のようになります。 ここに画像の説明を入力してください

それから私は、マンカラとワリのゲームで勝者が決定される方法はまったく同じであり、コードが複製されるだろうと思いました。これは、定義によって、「1つのルール、1つの場所」またはDRY原則の違反であるとは思わない。マンカラのルールの変更は、Wariのルールも自動的に変更されることを意味しない。それにもかかわらず、教授から得たフィードバックから、私は別のデザインを見つける印象を受けました。

それから私はこれを思いついた: ここに画像の説明を入力してください

各ゲーム(Mancala、Wari、Kalahなど)には、各ルールのインターフェイスのタイプの属性があります。つまりWinnerDeterminer、Mancala 2.0バージョンがあり、Mancala 1.0と同じ場合は、勝者が決定される方法を除きます。 Mancalaバージョンを使用します。

戦略パターンとしてのこれらのルールの実装は確かに有効だと思います。しかし、本当の問題は、さらに設計したいときに起こります。

テンプレートメソッドパターンについて読んで、すぐにこの問題に適用できると思いました。ユーザーが移動したときに実行されるアクションは常に同じであり、同じ順序で、すなわち:

  • 穴に石を置く(これはすべてのゲームで同じであるため、テンプレートメソッド自体に実装されます)
  • 移動の結果を決定する
  • 前の動きのためにゲームが終了したかどうかを判断する
  • ゲームが終了したら、誰が勝ったかを判断する

これらの最後の3つの手順は、すべて上記の私の戦略パターンに含まれています。これら2つを組み合わせるのに苦労しています。私が見つけた1つの可能な解決策は、戦略パターンを放棄し、以下を行うことです: ここに画像の説明を入力してください

戦略パターンとこれの間のデザインの違いは本当に見られませんか?しかし、テンプレートメソッドを使用する必要があることは確かです(ただし、戦略パターンを使用する必要があることは確かです)。

また、誰がTurnTemplateオブジェクトの作成を担当するかを判断することもできませんが、戦略パターンでは、抽象的なファクトリパターンを使用して簡単に作成できるオブジェクトのファミリ(3つのルール)があると感じています。私はそれからだろうMancalaRuleFactoryWariRuleFactoryなどと彼らは私がバックアップ規則や手の正しいインスタンスを作成することになりRuleSet、オブジェクトを。

戦略+抽象ファクトリパターンをRuleSet使用し、3つのルールのアルゴリズムを持つオブジェクトがあるとします。これでテンプレートメソッドパターンを使用できると感じる唯一の方法は、このRuleSetオブジェクトをに渡すことTurnTemplateです。そのとき表面化する「問題」は、の具体的な実装を必要としないことでありTurnTemplate、これらのクラスは時代遅れになります。の保護されたメソッドでは、TurnTemplate単に呼び出すことができますruleSet.determineWinner()。結果として、TurnTemplateクラスは抽象的ではなくなりますが、具体的になる必要がありますが、それでもテンプレートメソッドパターンですか?

要約すると、私は正しい方法で考えているのですか、それとも簡単な何かを見逃していますか?順調に進んでいる場合、戦略パターンとテンプレートメソッドパターンをどのように組み合わせるのですか?


1
私はあなたの考えがほとんどスポットであると思います。教授から得たフィードバックがそれを検証しない場合は、欠陥を指摘するか、答えで何を求めているかのヒントを彼に求めてください。心を読もうとするのをやめてください。教授(そして後にユーザー)が望んでいることを想定することは、おそらくあなたができる最悪のことです。
マルジャンヴェネマ

...マンカラとワリのゲームでは、勝者が決定される方法はまったく同じであり、コードが複製されます。」なぜコードが複製されることを意味するのですか?両方のクラスが同じ関数を呼び出すようにします。
D Drmmr

回答:


6

デザインを見てみると、最初と3回目の反復はどちらもよりエレガントなデザインに見えます。しかし、あなたはあなたが学生であり、あなたの教授があなたにいくつかのフィードバックを与えたと言います。あなたの課題やクラスの目的が何であるかを正確に知ることなく、または教授が提案したことに関する詳細な情報なしで、私は一言で以下に言うことをすべて取ります。

最初のデザインでは、RuleInterface各プレーヤーのターンの処理方法、ゲームオーバーかどうかの判断方法、ゲーム終了後の勝者の決定方法を定義するインターフェイスとして宣言します。それは、変化を経験するゲームファミリーへの有効なインターフェースのようです。ただし、ゲームによっては、コードが重複している場合があります。1つのゲームのルールを変更する柔軟性は良いことであることに同意しますが、コードの重複は欠陥のためにひどいことも主張します。実装間で欠陥のあるコードをコピー/貼り付けし、バグがある場合、異なる場所で修正する必要がある複数のバグがあります。実装をさまざまなタイミングで書き換えると、さまざまな場所に欠陥が生じる可能性があります。どちらも望ましくありません。

2番目のデザインは、深い継承ツリーを備えたかなり複雑に見えます。少なくとも、この種の問題を解決するために私が期待するよりも深い。また、実装の詳細を他のクラスに分割し始めています。最終的には、ゲームをモデリングして実装します。移動の結果、ゲームの終了、勝者を決定するためにルールをミックスアンドマッチする必要がある場合、これは興味深いアプローチかもしれません。 。あなたのゲームは明確に定義されたルールのセットであり、できるだけ多くのゲームを個別のエンティティにカプセル化するようにします。

3番目のデザインは、私が一番気に入っているデザインです。私の唯一の懸念は、適切な抽象化レベルではないということです。今、あなたはターンをモデリングしているように見えます。ゲームの設計を検討することをお勧めします。ある種のボード上で、石を使って動きをするプレイヤーがいると考えてください。ゲームでは、これらのアクターが存在する必要があります。そこから、あなたのアルゴリズムではありませんdoTurn()が、playGame()それが終了した後、最終的な動きへの最初の動き、からなっています。すべてのプレイヤーの動きの後、ゲームの状態を調整し、ゲームが最終状態にあるかどうかを判断し、そうであれば勝者を決定します。

最初と3番目のデザインを詳しく見て、それらを操作することをお勧めします。プロトタイプの観点から考えることも役立ちます。これらのインターフェイスを使用するクライアントはどのように見えますか?実際にゲームをインスタンス化してゲームをプレイするクライアントを実装するために、1つの設計アプローチがより理にかなっていますか?相互作用の対象を理解する必要があります。あなたの特定のケースでは、それはGameクラスと他の関連要素です-単独で設計することはできません。


あなたは学生だと言っているので、私がソフトウェア設計コースのTAだった頃からいくつかのことを共有したいと思います。

  • パターンは、過去に機能したものをキャプチャするだけの方法ですが、他のデザインで使用できるポイントまで抽象化するものです。設計パターンの各カタログは、パターンに名前を付け、その意図と使用できる場所、および最終的に設計を制約する状況を説明します。
  • デザインには経験が必要です。設計を上手に行うための最良の方法は、単にモデリングの側面に焦点を合わせるのではなく、そのモデルの実装に何が入るかを理解することです。最もエレガントなデザインは、簡単に実装できない場合や、システムの大規模なデザインや他のシステムに収まらない場合に役立ちます。
  • 「正しい」または「間違っている」デザインはほとんどありません。設計がシステムの要件を満たしている限り、それは間違いありません。各要件から、システムがその要件をどのように満たすかを表す何らかの表現へのマッピングがあれば、設計は間違っていません。現時点では、柔軟性や再利用性、テスト可能性、保守性などの概念については定性的です。

すばらしいアドバイス、特に、抽象化のレベルが間違っているという事実についてのあなたの指摘に感謝します。私は今それをGameTemplateはるかに良い感じに変更しました。また、ファクトリメソッドを組み合わせて、プレーヤーやボードなどを初期化することもできます。
Mekswoll

3

あなたの混乱は正当化されます。問題は、パターンは相互に排他的ではないということです。

テンプレートメソッドは、StrategyやStateなど、他の多くのパターンの基礎です。基本的に、Strategyインターフェースには1つ以上のテンプレートメソッドが含まれており、それぞれが(少なくとも)doAction()メソッドのようなものを持つために、戦略を実装するすべてのオブジェクトを必要とします。これにより、戦略を相互に置き換えることができます。

Javaでは、インターフェイスはテンプレートメソッドのセットにすぎません。同様に、抽象メソッドは基本的にテンプレートメソッドです。このパターンは(とりわけ)言語の設計者によく知られているため、組み込みました。

@ThomasOwensは、特定の問題へのアプローチに関する優れたアドバイスを提供します。


0

デザインパターンに気を取られている場合は、最初にゲームのプロトタイプを作成することをお勧めします。その場合、パターンがすぐに飛び出します。最初にシステムを完全に設計してから実装しようとすることは本当に可能またはお勧めではないと思います(同様に、最初にプログラム全体を書いてからコンパイルしようとすると、少しずつやるのではなく困惑します) )問題は、ロジックが対処しなければならないすべてのシナリオを考える可能性が低いことであり、実装フェーズ中にすべての希望を失うか、元の欠陥のあるデザインに固執してハックを導入するか、さらにはさらに悪いことに、何も提供しません。


2
一方で、一般的に、私はあなたに同意し、これは本当にOPの問題を解決答えではありません。「それをしないでください」という答えはありますが、実行可能な代替手段を提供しない場合は、かなり貧弱なものです。
ヤニス

@YannisRizosそして、私もあなたに同意しますが、私はまだ彼が適切なアドバイスをしたと思います(アドバイスが正しい言葉でない場合は洞察力)。そのため、+ 1。とはいえ、はい、それは技術的に非常に有用な答えではありません。
買った777

-1

真ちゅう製のtoに取りかかりましょう。Gameインターフェイス、デザインパターン、抽象クラス、UMLはまったく必要ありません。

UI、シミュレーション、その他のような適切な量のサポートクラスがある場合、基本的にゲームロジック固有ではないコードはすべて再利用されます。さらに、ユーザーはゲームを動的に変更しません。ゲーム間で30Hzでフリップすることはありません。1つのゲームを約30分間プレイします。したがって、「動的な」ポリモーフィズムは実際にはまったく動的ではありません。かなり静的です。

したがって、ここに行く賢明な方法は、C#ActionやC ++ のような一般的な機能の抽象化を使用してstd::function、1つのMancala、1つのWari、および1つのKalahクラスを作成し、そこから進むことです。

std::unordered_map<std::string, std::function<void()>> games = {
    { "Kalah", [] { return Kalah(); } },
    { "Mancala", [] { return Mancala(); } },
    { "Wari", [] { return Wari(); } }
};
void play() {
    std::function<void()> f;
    f = games[GetChoiceFromUI()];
    f();
    if (PlayAgain()) return play();
}
int main() {
    play();
}

できた

ゲームを呼び出しません。ゲームはあなたを呼び出します。


3
これがどのように役立つかわかりません。この質問をする人は学生です。彼は、設​​計原理と2つの設計パターン間の相互作用について明示的に尋ねています。設計パターンやUMLの必要がないと言うのは、業界の一部の事例では真実かもしれませんが、設計モデル、設計パターン、およびUMLについて考えることが、演習のポイントになる場合があります。
トーマスオーエンズ

3
それらを適用する場所がないため、それらについて言うことは何もありません。設計パターンを使用して設計を完全にコックアップする方法について考える時間を費やす意味はありません。
DeadMG

4
SoftDevには、使用されている設計パターンが単に問題を解決することよりも重要であると考えられる心配/迷惑な傾向があります。オーバーエンジニアリングなどがあります。
ジェームズ

2
@DeadMG:どちらも基本的に正しいのですが、OPの場合、問題は試験に合格しており、その問題の解決策はデザインパターンとUMLを使用することです。どんなに私たちが正しいとしても、教授が彼の解決策に同意しなければ、彼は合格しません。
ゴランジョヴィック

2
私の人生は、私はいつもそれがなんと、「真鍮の鋲が」道より理にかなって...「真鍮税」だと思ってきました。lol
buy777
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.