Как можно описать взаимоотношения каменной бумаги с ножницами между тремя предметами?

Допустим, у меня есть следующая структура:

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

Цель состоит в том, чтобы сделать функцию (или метод) Hand::compareHands(Hand $hand1, Hand $hand2) , которая вернет выигрышную руку в матче с бумагами-ножницами.

Это было бы очень легко с кучей if s, но дело в том, чтобы иметь более надежную структуру, которая опирается на полиморфизм, а не на процедурный код.

PS это делается в фактическом производственном коде, если кто-то спрашивает. Это не вызов или домашнее задание. (Это не рок-бумага-ножницы, но вы понимаете).

Единственный характер вашей руки в том, что она избивает один другой.

Затем вы хотите не повторять код, имея один конкретный тип для каждой руки, поэтому вам нужно параметризовать. В зависимости от уровня свободы, который вы можете разрешить, это может быть так же просто, как защищенный член:

 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'; } 

Я думаю, что это стандартный шаблонный шаблон шаблона здесь, в очень простой форме.

Сравните это с ответом Лузитанана, который должен получить кредиты за фактический код, я просто немного отсортировал его. Но только очень мало.

Кроме того, мне нужно дать кредиты @Leigh за гораздо лучшую функцию и присвоение параметров . Это должно уменьшить потребность в комментариях.

Второй вариант, предложенный Лузистаном, может быть представлен в виде стратегии . Это также несколько прямолинейно:

 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 {} 

Кредиты для этого кода идут в гордон (рядом с Лузистаном).

Как насчет этого?

 class Scissors extends Hand implements Beats<Paper> {} 

где Beats <> – это общий интерфейс, чья подпись выглядит так:

 interface Beats<Hand> {} 

Из PHP-чата

ООП-стиль

 <?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; }