Подтвердить что ты не робот

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

Скажем, у меня есть следующая структура:

abstract class Hand {}

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

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

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

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

4b9b3361

Ответ 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 answer, который должен получить кредиты за фактический код, я просто немного отсортировал его. Но только очень мало.

Кроме того, мне нужно предоставить кредиты @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 {}

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

Ответ 2

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

class Scissors extends Hand implements Beats<Paper> {}

где Beats < > - это общий интерфейс, подпись которого выглядит следующим образом:

interface Beats<Hand> {}

Ответ 3

Из чата 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;
}