如何描述这 3 件物品之间的石头剪刀布关系?

How can one describe a rock-paper-scissors relationship between 3 items?

提问人:Madara's Ghost 提问时间:10/4/2012 最后编辑:Madara's Ghost 更新时间:10/4/2012 访问量:1403

问:

假设我有以下结构:

abstract class Hand {}

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

目标是制作一个函数(或方法),它将在石头剪刀布比赛中返回获胜的手牌。Hand::compareHands(Hand $hand1, Hand $hand2)

对于一堆 s,这很容易,但关键是要有一个更健壮的结构,它依赖于多态性而不是过程代码。if

P.S. 如果有人问,这是在实际的生产代码中完成的。这不是某种挑战或家庭作业。(这不是真正的石头剪刀布,但你明白了)。

PHP 哎呀

评论

0赞 tereško 10/4/2012
的实例不比较自己。这就是为什么我们有.HandBrains
0赞 Madara's Ghost 10/4/2012
@tereko:有意思,有涉及的解决方案吗?Brain

答:

5赞 Chris Laplante 10/4/2012 #1

这个怎么样?

class Scissors extends Hand implements Beats<Paper> {}

其中 Beats<> 是一个通用接口,其签名如下所示:

interface Beats<Hand> {}

评论

0赞 Madara's Ghost 10/4/2012
我不是用Java编程(上面是伪代码),实际代码是PHP。虽然这看起来很有趣,但你能详细说明一下吗?
1赞 Chris Laplante 10/4/2012
哦,好的。我知道PHP最近添加了一些特征,就像接口一样,但我不知道它是否可以做泛型。既然你问的是一般的 OOP,而不是专门的 PHP,我就保持原样。
0赞 Madara's Ghost 10/4/2012
我很欣赏社区对此的意见,但我在 PHP 中没有通用接口:((另外我不知道如何在 Java 中使用它们)。因此,虽然它可能很好,但这并不是我正在寻找的答案:)
3赞 Lusitanian 10/4/2012 #2

从PHP聊天

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赞 Madara's Ghost 10/4/2012
看起来很有趣,但我想尽量避免一遍又一遍地重复代码。
12赞 hakre 10/4/2012 #3

你的手的唯一性质是它正在击败另一只手。

然后,您希望在每个手工表单具有一个具体类型时不重复代码,因此您需要参数化。根据您可以允许的自由度级别,这可以像受保护成员一样简单:

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 的答案进行比较,谁应该获得实际代码的学分,我只是重新排序了一下。但只有很少。

此外,我需要感谢@Leigh更好的函数和参数命名。这应该会减少评论的需要。

Lusistanian建议的第二种选择可以用策略模式来表示。它也有些直截了当:

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赞 John Ballinger 10/4/2012
超级漂亮。我认为我能让你失望的唯一方法是缺乏评论,但这是一个非常漂亮的解决方案。
0赞 hakre 10/4/2012
@JohnBallinger:请分享您错过的一条或另一条评论,我很乐意添加这些评论。
0赞 raina77ow 10/4/2012
+1,因为它很清楚,而不是象牙塔里的。顺便说一句,也许明智的做法是显示获胜、平局和失败的不同结果,这检查了 和 .$hand instanceof $this->beats$this instanceof $hand->beats
2赞 hakre 10/4/2012
啊,好吧,没错,可以在这里记录逻辑(特别是 的返回值)。但是我认为更重要的是展示如何使用抽象函数和受保护的成员来使其工作。具体的算法并不那么重要(正如OP所写的那样)。 是 bool 并告诉与(参数)相比的手牌是否被殴打。compareHandcompareHand
0赞 hakre 10/4/2012
我现在改了,措辞应该更清楚,谢谢@Leigh