2011-06-23 18 views
1

私はクラスとextentionsのシリーズを持っている拡張今Javaは質問

Class Base2 { 
    Base1 base1; 
    } 

Class Base2Extend1{ 
    base1 = new Base1Extend1();} 

Class Base2Extend1{ 
    base1 = new Base1Extend2();} 

を、BASE2はBASE1のインスタンス化にいくつかの作業を実行します。

また、Base2Extend1とBase2Extend2は、それぞれの拡張クラスで作業を行う必要があります。たとえば、サブクラスにのみ存在する特定のメソッドを呼び出します。 Base2の拡張クラスで毎回キャストを使わなくてもこれを行う方法はありますか?


正確な場合には、このようなものです:ユーザーと「コンピュータ」:

それは選手の2種類があるカードゲームです。 これらの両方に適用される基本メソッドはたくさんあります(pickupCards()、discardCards()など)。 次に、ユーザ専用のメソッド(checkMove()など)と、「コンピュータ」(doTurn()など)固有のメソッドがあります。

次に、ユーザーのカードを保持するオブジェクトplayerHandがあります。 もう一度手を扱う基本メソッド(addCards()、removeCards()など)があります。 次に、ユーザーの手のみのための特定のメソッド(checkCards()など)と、「コンピュータ」(decideWhatToDo()など)に固有のメソッドがあります。

今、私はクラスを単にマージする方が正しいかもしれないと考えています。 Base1とBase2を1つのクラスにしてから、サブクラスに対して同等のマージを行います。

+0

'class Java extends Question'?うーん... – mre

+0

'Base2Extend2 extends Base1'という名前は正しいですか? – oliholz

+0

C++のように、クラスが他のクラスの「友人」になれるかどうか確認しようとしていますか? –

答えて

4

この場合、毎回キャストするか、より特定のタイプのフィールド/変数を作成する必要があります。

私はどこにいるのか分かりませんが、このデザインには注意してください。多くの場合、継承を間違って使用したということがよくあります。

スーパークラスで定義されたタスクのために新しいメソッドをサブクラスで呼び出す必要がある場合は、そのサブクラスが親の契約(Google Liskovs置換の原則)に違反していることを意味します。

サブクラスが実際に親のものと相互作用しない新しい機能を持っている場合は、最初から明示的にサブクラスを使用することができます(つまり変数宣言)。実際に関連していない場合は、継承の代わりに新しいクラスとコンポジションを使用してください。


これはかなり広範ですが、このカードゲームのデザインはどうですか?私はそれが何回かのターンでトリック・タッキング・ゲームであると仮定します。それは、移動が有効かどうか、そして誰がそのトリックを取るかを確認する必要があります。カードを破棄、追加、削除することでゲームの意味が分かりません。

  • Playerクラスを作成しますが、それ以上の名前はありません。それはまた、カードのコレクションを持つことができます。
  • もう1つのクラスは、何が起こっているかを調整し、ルールをチェックするGameControllerです。プレイヤー(プレイヤー、カード)は、プレイヤーの移動を覚えてカードを移動させ、そのカードをプレイしたことを覚えておくような方法をとることができます:
    • 各ターンの最後にスコアを計算するscore()。
  • もう一つのクラスはCPUControllerです。それが、AIが生きる場所です。
  • 最後に、メインループ。

メインループは次のように仕事ができる:

controller.shuffle(); 
// Either of: 
// 1. push: controller.shuffle() calls player.pickupCards(cards) 
// 2. pull: main loop calls player.pickupCards() which in turn calls controller.giveMeMyCards() 

while(playersHaveMoreCards()) { 
    awaitPlayerMove(); 
    // Now is the only time when player can make a move. If there is some 
    // GUI the main loop could `wait()` on a mutex (until it's awoken when 
    // player makes move), in text mode it could simply await input from keyboard. 

    // When player makes a move, it calls controller.playCard(player, card) 

    cpuController.move(); 
    // Again, when it controller calculates its move, eventually it simply calls controller.playCard() 

    controller.score(); 
} 
announceWinner(controller.getScore()); 

を、ここにあなたがこのアプローチを得るものです:

  • ゲームのルールとスコアリングに関連するすべてのものが一つの場所にあり、それがGameControllerにある唯一のものです。把握して維持するのは非常に簡単です。
  • CPU AIについても同様です。それはすべて1つの場所にあり、CPUControllerにある唯一のものはAIの実装です。
  • 人間とCPU、CPUとCPU、人間と人間のどちらを持っているかにかかわらず、コントローラーの外側はまったく同じです。マルチプレイヤーが必要な場合は、ここで変更するのはメインループだけです(それは簡単な変更です)。
  • 別のトリックテイクゲームを実装したい場合は、GameController(異なるルール)とCPUController(異なるAI)を変更することだけが可能です。
  • AIの別の実装(よりスマートなアルゴリズム)を望むなら、CPUControllerを置き換えるだけでAIを実装できます。

最後の2つのケースは継承が理にかなっています。 CPUControllerとGameControllerが同じインタフェースを持つようにしたいので、メインループとPlayerは異なる実装のコントローラでシームレスに動作することができます。

これらのデザイン原則についてもっと知りたい場合は、Google for SOLIDをご覧ください。これはすべての例です。 :-)

+0

質問にいくつかの詳細を追加して、それが正しい方法であるかどうか疑問に思っています。 – theblitz

+0

@theblitz更新された回答を参照してください。 –

+0

ゲームはトリックゲームではなく、破棄/ピックアップゲームです。 AIがまだ移動していない場合、ユーザは移動後に再び移動することができることがある。この理由から私はループから取り除き、UIおよび/またはpostDelayedによって駆動されるようにしなければなりませんでした。ループを行う代わりに、各プレイヤーは、そのプレイヤーが順番を終えたときに次のコントローラーにコントロールを渡します。 – theblitz

1

いいえありません。キャストを実行する必要があります。そうしないと、コンパイラは関数が存在するかどうかを判断できません。

サブクラスの関数が同じ場合は、それらをインターフェイスで実装し、サブクラスでそれらを実装してベースクラスを拡張することができます。その後、単にインターフェイスにキャストすることができますが、キャストを行う必要があります。

+0

ジェネリックではキャストが実行されますが、開発者が明示的に行ってはいけません。 – Nick

1

ジェネリックについて考え、キャストを避けるために:

class Base2<T extends Base1> { T base1; } 

// base1 does not need to be cast to Base1Extends1 
class Base2Extends1 extends Base2<Base1Extends1> { base1 = new Base1Extends1(); } 

// base1 does not need to be cast to Base1Extends2 
class Base2Extends2 extends Base2<Base1Extends2> { base1 = new Base1Extends2(); } 
0

あなたは、例えばを使用してクラスをパラメータ化したい場合がありますフィールドT base1を使用する。コンパイラは、T => Base1Extend1またはBase1Extend2を使用していることを知っているので、キャストする必要はありません。

+0

バガー、ニック私にそれを打つ:) –