2012-10-03 26 views
12

のは、私は次のような構造を持っているとしましょう:3つのアイテムの間にロックペーパー - はさみ関係をどのように記述できますか?

abstract class Hand {} 

class Rock extends Hand {} 
class Paper extends Hand {} 
class Scissors extends Hand {} 

目標はじゃんけんの試合で勝つ手を返す関数(またはメソッド)Hand::compareHands(Hand $hand1, Hand $hand2)を作ることです。

ifの束では非常に簡単ですが、要点は手続き型コードではなく多形性に依存するより堅牢な構造を持つことです。

P.S.誰かが尋ねている場合、これは実際の生産コードで行われます。これは何らかの挑戦や宿題ではありません。 (それは本当にロックペーパーのはさみではありませんが、ポイントを得ることができます)。

+0

「ハンド」のインスタンスは、自分自身を比較しません。だから私たちは '脳 'を持っている。 –

+0

@tereko:興味深いことに、「脳」を含む解決策がありますか? –

答えて

12

あなたの手の唯一の性質は、他の1つを打っているということです。

次に、ハンドフォームごとに1つの具体的なタイプを持つ間にコードを繰り返さないようにしたいので、パラメータ化する必要があります。あなたが許可することができます自由のレベルに応じて、これは保護されたメンバーのように簡単になります

abstract class Hand { 

    protected $beats; 

    final public function beats(Hand $opponent) { 

     return $opponent instanceof $this->beats; 
    } 
} 

class Rock extends Hand { 

    protected beats = 'Scissors'; 
} 

class Paper extends Hand { 

    protected beats = 'Rock'; 
} 

class Scissors extends Hand { 

    protected beats = 'Paper'; 
} 

私は、これは非常に単純な形でここに標準テンプレートメソッドパターン、だと思います。

これをLusitanian's answerと比較すると、実際のコードのクレジットが得られるはずです。少しだけ並べ替えただけです。しかし、ごくわずかです。

さらに、私は@Leigh for the far better function and parameter namingにクレジットを与える必要があります。これはコメントの必要性を減らすはずです。

Lusistanianが提案する第2の代替案は、戦略パターンで表すことができます。また、やや単純です:

class EvaluateHands 
{ 
    private $rules; 

    public function __construct(array $rules) 
    { 
     $this->rules = $rules; 
    } 

    public function compareHands(Hand $hand1, Hand $hand2) 
    { 
     return $this->rules[get_class($hand1)] === get_class($hand2) ? $hand1 : $hand2; 
    } 
} 

new EvaluateHands(
    array(
     'Rock' => 'Scissors', 
     'Paper' => 'Rock', 
     'Scissor' => 'Paper' 
    ) 
); 

両手の間の比較は完全にも構成可能であるEvaluateHandsタイプにカプセル化されています(もしゲームの変化の規則)手が同じにとどまる一方で、:

abstract class Hand {} 

class Rock extends Hand {} 

class Paper extends Hand {} 

class Scissors extends Hand {} 

このコードのクレジットはgordon(Lusistanianの隣)です。

+0

超美しい。私はあなたを下げることができる唯一の方法は、コメントの欠如が非常にきれいな解決策だと思う。 –

+0

@JohnBallinger:あなたが1つまたは他のコメントを見逃している箇所を共有してください。それらを追加してもよろしいですか? – hakre

+0

+1、それはかなり鮮明で、象牙の塔ではないからです。 sidenoteとして、$ hand instanceof $ this-> beatsと$ this instanceof $ hand-> beatsの両方をチェックする、勝利、引き分け、敗北のために異なる結果を表示するのが賢明でしょう。 – raina77ow

5

これはいかがですか? <ビート

class Scissors extends Hand implements Beats<Paper> {} 

>その署名のように見える一般的なインタフェースです:PHPチャットから

interface Beats<Hand> {} 
+0

私はJavaでプログラミングしていません(上のコードは擬似コードです)。実際のコードはPHPです。これは興味深いようですが、あなたはそれについて詳しく説明できますか? –

+1

ああ、よろしくお願いします。私は最近、PHPの特性を知っていますが、これはインターフェースに似ていますが、ジェネリックを行うことができるかどうかはわかりません。あなたは一般的にOOPについて尋ねてきたので、特にPHPではなく、これをそのまま残しておきます。 –

+1

+1、確かに正しいアイディア – Lusitanian

3

OOPスタイル

<?php 
interface Hand { 
    function beats(Hand $hand); 
} 

class Rock implements Hand { 
    public function beats(Hand $hand) { 
     return $hand instanceof Scissors; 
    } 
} 
class Paper implements Hand { 
    public function beats(Hand $hand) { 
     return $hand instanceof Rock; 
    } 
} 

class Scissors implements Hand { 
    public function beats(Hand $hand) { 
     return $hand instanceof Paper; 
    } 
} 

シンプルな機能

<?php 
const PAPER = 1; 
const ROCK = 2; 
const SCISSORS = 3; 

function whichHandWon($hand1, $hand2) { 
    $winners = [PAPER => ROCK, ROCK => SCISSORS, SCISSORS => PAPER]; 
    return intval($winners[$hand1] !== $hand2) + 1; 
} 
+0

興味深いですが、コードを何度も何度も繰り返さないようにしたいと思います。 –

関連する問題