扑克牌:应该是枚举、结构还是类
本文关键字:结构 枚举 扑克牌 | 更新日期: 2023-09-27 18:29:22
我正在设计游戏网站,许多(希望是数千)玩家将在这里同时玩某些纸牌游戏。牌组是标准52牌组。每张牌都有一套和一个等级。这些牌将一直被洗牌、发牌、挑选、订购、玩。我的问题是,Card
应该是枚举、结构还是类?
-
对于枚举:让每个卡都是一个字节0..51。所以一张牌只会占用很少的空间。您可以将一只手表示为一个8字节的位集。当需要时,你可以很快计算出一张牌的花色和等级:即花色(n)=n/13。这将非常有效。如果您需要为Cards编写方法,请通过扩展方法编写。
-
对于结构:不,这就像写机器代码。卡片是一种简单的结构,可以容纳很少的数据,是不可变的,很小。它没有太多的行为,所以将其作为一个结构,并将其视为一个被动数据结构。当需要时,你可以很快从一张给定的卡片中计算出0..51中的索引。
-
对于类:不,这不是一种面向对象的思维方式。制作一个Card类。使其不可变。正好创建52个实例。让卡池保存这些实例。因此,当一个人需要黑桃皇后时,他会向卡池索要。即使有成千上万的游戏在进行,也只有一个黑桃皇后。如果你愿意,在Card中存储一个索引字段0..51。
我倾向于最后一种选择(上课),但我不确定。我不太担心表现;一路上我可能会犯更严重的错误。我担心的是我的整个观点可能是错误的;也许这是一个非常容易的决定,我犹豫了,因为我缺乏其他人所拥有的一些知识。
你觉得怎么样?
编辑:关于纸牌的行为。我认为一张牌只会知道其他牌。例如,它可以定义"谁在桥上打败谁"的偏序。它不需要知道任何关于甲板的信息。这将是服务器端代码;当然,它不需要知道在屏幕上画自己。
在决定引用类型还是值类型时,你应该问自己的基本问题当然是我在逻辑上建模的是值,还是引用的东西这就是为什么值类型和引用类型首先被称为"值类型"answers"引用类型"。
你是否打算将"卡片"作为参考,就像对待有身份的实物一样?例如,假设您正在为Canasta建模,它同时使用两个标准牌组进行游戏。你会想跟踪两个不同的黑桃皇后,并将它们视为参考的不同吗?
还是将它们视为值,就像对待数字一样?你永远不会说"这里的数字6和那里的数字6不同",因为数字只是根据它们的值来区分的。
为卡制作struct
或class
,卡的值应该是enum
,卡的套装也是enum
。
要制作一副牌,可以使用class
,它包含card
的列表,具有Shuffle
等操作。
您可以将每张牌表示为一个数字,然后将它们封装在枚举和类中。如果0到51之间的每个数字都代表一张卡,那么你就可以有一个枚举:
public enum Card
{
HeartsAce = 0,
Hearts2 = 1,
// ... and so on
}
int nCard = 16;
Card myCard = (Card)nCard;
你也可以有一个类:
public class Card
{
private readonly int _nSuitNumber = 0;
private readonly int _nCardNumber = 0;
public Card(int a_nNumber)
{
_nSuitNumber = a_nNumber / 13; // 1 = Hearts, 2 = Diamonds ...
_nCardNumber = a_nNumber % 13; // 1 = ace, 2 = two
}
}
或者最好将它们结合起来。
public class Card
{
private readonly Suit _suit;
private readonly Value _value;
public Card(int a_nNumber)
{
_suit = (Suit)(a_nNumber / 13); // 0 = Hearts, 1 = Diamonds ...
_value = (Value)(a_nNumber % 13 + 1); // 1 = ace, 2 = two
}
public Suit Suit
{
get { return _suit; }
}
public Value Value
{
get { return _value; }
}
public int ToNumber()
{
return (int)_suit * 13 + ((int)_value - 1);
}
}
public enum Suit
{
Hearts = 0,
Diamonds = 1,
Clubs = 2,
Spades = 3
}
public enum Value
{
Ace = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 11,
Queen = 12,
King = 13,
}
有了这个类,您就可以同时获得数字(0-52)和类的好处。这个类不过是一个数字的包装器。您可以根据需要向类中添加任何操作、方法和属性。当你存储或传输数据时,你只需要使用数字。
我不相信你在这里问的问题是对的。如何代表你的卡片应该由你想用你的卡片做什么来决定;通过回答现在如何表示你的卡片的问题,你会自动约束自己,以解决以后遇到的其他更有趣/更具挑战性的问题。
你真正应该考虑的事情是如何实现你的程序行为。例如
-
当发"牌"时会发生什么?你想表示一张卡在持有这些卡的实体之间物理移动吗?(例如,牌组、玩家的手牌、弃牌堆、桌子等),还是你希望这些实体只引用一个中央卡片库?或者使用完全独立的计数机制?
-
你打算如何"订购"卡片?你选择实施的游戏可能有不同的规则(例如,王牌低还是高?皇家套装都有相同的价值吗?会有王牌吗?)这些决定可能是在逐个游戏的基础上决定的,甚至在游戏本身发生了变化,因此,简单地根据牌的顺序对牌进行单一定义可能不是一个好的抽象概念。
-
你试图代表什么样的行为和游戏规则,这将取决于牌?卡片本身会有依赖关系吗?
-
卡片是要参与用户交互,还是纯粹是程序中定义卡片游戏的部分使用的逻辑对象?
-
你有没有想过使用不同种类卡片的可能性?你在最初的问题中提到了一个类,但如果一张卡要定义任何类型的状态、行为或与其他类交互,那么在担心细节之前,你可能首先想定义一个卡接口。
-
作为对你问题的直接回答——我个人认为,除了基于猜测和个人偏好的观点之外,你还没有提供足够的信息来回答问题;这没关系,因为在开始实现之前,通常很难找到所有的答案。你可能想试试这三种,看看哪一种最适合你的需求。不要通过提前做出这些决定来限制你的设计。
我认为这个问题可以从逻辑上得到回答。我将从面向对象的角度来看待它。我认为一张牌有两个子元素,套装和价值。这些可能是enum
。手(类)可能应该包含一个卡片列表,这让我相信最好将卡片作为结构或类。为了确定一只手是否有一对,这将是手类的一个函数。
由于多人玩不同类型的游戏,每副牌都必须知道它有什么类型的牌以及有多少张。但即使在这种情况下,卡片本身也是不可变的。
我会使用枚举来处理一个类或结构,并在它上面放置一个内部构造函数:
publlic class PlayingCard
{
internal PlayingdCard(CardSuit suit, CardFace face)
{
this.Suit = suit;
this.Face = face;
}
public CardSuit Suit { get; private set; }
public CardFace Face { get; private set; }
}
定义你的Deck类来管理卡片,但(最重要的)让它在构建过程中分配卡片:
public class Deck
{
private List<PlayingCard> cards = new List<PlayingCard>();
public Deck()
{
for(var i = 0; i < 4; i++)
{
for (var j = 1 to 13)
{
this.cards.Add(new PlayingCard((CardSuit)i, (CardFace)j));
}
}
}
public void Shuffle() { /* implement me */ }
public PlayingCard GetNextCard() { /* implement me */ }
}
通过这种方式,甲板尺寸是固定的,人脸和西装是用尽可能少的代码创建的(尽管我相信有人能想出一种方法来LINQify)。
您可能会考虑要构建什么抽象。如果一张卡是由其他功能传递的低级对象,那么更简单的enum
可能更合适,但如果你想让一张卡本身能够draw()
或其他什么,那么你需要更复杂的东西。这张卡如何适合你的申请?