扑克牌:它们应该是枚举还是结构或类?

我正在设计游戏网站,许多(希望成千上万)玩家将同时互相玩某些纸牌游戏。 甲板是标准牌52牌。 每张卡都有一套西装和一个等级。 这些卡片将被随机洗牌,处理,挑选,订购和播放。 我的问题是, Card应该是枚举,结构还是类?

  • 对于枚举:让每张卡都是字节0..51。 因此卡片占用的空间非常小。 您可以将手表示为8字节的位集。 您可以在需要时非常快速地计算给定卡的套装和等级:即suit(n)= n / 13。 这将非常有效。 如果需要为Cards编写方法,请通过扩展方法编写。

  • 对于struct:不,这就像编写机器代码一样。 卡是一种简单的结构,保存的数据非常少,不可变,很小。 它没有太多行为,因此将其作为结构并将其视为被动数据结构。 当需要时,您可以非常快速地从给定卡计算0..51中的索引。

  • 对于课堂:不,这不是一种面向对象的思维方式。 制作卡片课程。 让它永恒不变。 准确创建52个实例。 让卡池保存这些实例。 因此,当需要黑桃皇后时,它会向卡片池询问。 即使有数千场比赛正在进行,也会有一个也只有一个黑桃皇后。 如果需要,可以在卡中存储索引字段0..51。

我倾向于最后一个选项(课程),但我不确定。 我不太担心表现; 在此过程中,我可能会犯更严重的错误。 我担心的是我的整个观点可能是错的; 也许这是一个非常简单的决定,我犹豫不决,因为我缺乏其他人拥有的一些知识。

你怎么看?

编辑:关于卡的行为。 我认为一张卡片只会知道其他卡片。 例如,它可以定义关于“谁击败谁在桥中”的部分顺序。 它不需要知道关于甲板的任何信息。 这将是服务器端代码; 当然,它不需要知道在屏幕上绘制等。

当你在引用类型或值类型之间进行决定时,你应该问自己的基本问题当然是我在逻辑上建模一个 ,或者一个被引用东西 这就是为什么值类型和引用类型首先被称为“值类型”和“引用类型”。

您是否计划将“卡片”作为参考处理,就像处理具有身份的物理对象一样? 例如,假设您正在为Canasta建模,它同时使用两个标准卡片组进行播放。 你是否想要跟踪两个不同的黑桃皇后并将它们视为不同的参考

或者你是否会将它们视为价值观 ,就像对待数字一样 ? 你永远不会说“这里的六号与那里的六号不同”,因为数字只能通过它们的数值来区分。

为卡制作structclass ,卡的值应为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)和课程。 这个类只不过是数字的包装器。 您可以根据需要向类中添加任何操作,方法和属性。 当您存储或传输数据时,您只需使用该号码。

我不相信你真的在这里问正确的问题。 如何代表您的卡片应该取决于您想要对卡片做什么; 通过回答现在如何代表您的卡片的问题,您将自动约束自己可以解决其他更有趣/更具挑战性的问题,这些问题将在以后遇到。

你真正应该考虑的事情是你将如何实现你的程序的行为。 例如

  • 处理“卡”会发生什么? 您想表示在持有这些卡的实体之间实际移动的卡吗? (例如,Deck,玩家手,弃牌堆,桌子等),或者您是否希望这些实体仅仅引用中央存储卡? 或者可能使用完全独立的计数机制?

  • 你怎么去“订购”卡? 你选择实施的游戏可能有不同的规则(例如Ace低或高?皇家套装都有相同的价值吗?会不会有王牌?)这些可能是逐个游戏决定的决定,甚至在游戏本身期间也发生了变化,因此根据它的排序简单地定义一张卡片可能不是一个好的抽象。

    • 您尝试表示哪种行为和游戏规则取决于卡? 卡本身是否有任何依赖?

    • 卡片是否会参与用户交互,还是纯粹是程序中定义纸牌游戏的部分所使用的逻辑对象?

    • 您是否考虑过使用不同种类卡片的可能性? 您在原始问题中提到了一个类,但如果一个卡要定义任何类型的状态,行为或与其他类交互,那么您可能首先想要在担心细节之前考虑定义Card接口。

作为您问题的直接答案 – 我个人认为您提供的信息不足以作为基于猜测和个人偏好的意见; 这没关系,因为通常很难在你开始实施之前提出所有的答案。 您可能想尝试所有三种,看看哪种最适合您的需求。 不要通过预先做出这些决定来限制您的设计。

我认为这个问题在逻辑上可以回答。 我将从面向对象的角度来看一下它。 我认为一张卡片有两个子元素,套装和价值。 这应该是enum 。 手(类)应该包含一张卡片列表,这使我相信最好将卡片作为结构或类。 为了确定一只手是否有一对,这将是手类的一个function。

由于多人玩不同类型游戏的游戏,每个牌组都必须知道它有哪种类型的牌以及牌数。 但即使在这种情况下,卡本身也是不可变的。

我会使用枚举的类或结构,并在其上放置一个内部构造函数:

 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 cards = new List(); 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()本身或其他任何东西,那么你需要更复杂的东西。 该卡如何适合您的应用程序?