Допустим, у меня есть следующая структура:
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; }