我正在设计游戏网站,许多(希望有数千名)玩家会相互同时玩某些纸牌游戏。甲板是标准牌52牌。每张卡都有一套西装和一个等级。这些卡片将被随机洗牌,处理,挑选,订购和播放。我的问题是,Card
应该是枚举,结构还是类?
对于枚举: 让每张卡都是字节0..51。因此卡片占用的空间非常小。您可以将手表示为8字节的位集。您可以在需要时非常快速地计算给定卡的套装和等级:即suit(n)= n / 13。这将非常有效。如果您需要为卡片编写方法,请通过扩展方法编写。
对于struct: 不,这就像编写机器代码一样。卡是一种简单的结构,保存的数据非常少,不可变,很小。它没有太多行为,因此将其作为结构并将其视为被动数据结构。当需要时,您可以非常快速地从给定卡中计算0..51中的索引。
上课时间: 不,这不是一种面向对象的思维方式。制作卡片课程。让它一成不变。准确创建52个实例。让卡池保存这些实例。因此,当需要黑桃皇后时,它会向卡片池询问。即使有数千场比赛正在进行,也会有一个也只有一个黑桃皇后。如果需要,可以在卡中存储索引字段0..51。
我倾向于最后一个选项(课程),但我不确定。我不太担心表现;在此过程中,我可能会犯更严重的错误。我担心的是我的整个观点可能是错的;也许这是一个非常简单的决定,我犹豫不决,因为我缺乏其他人拥有的一些知识。
您怎么看?
编辑:关于卡片的行为。我认为一张卡片只会知道其他卡片。例如,它可以定义关于“谁击败谁在桥中”的部分顺序。它不需要知道关于甲板的任何信息。这将是服务器端代码;当然,它不需要知道在屏幕上绘制等等。
答案 0 :(得分:13)
在决定引用类型或值类型时,您应该问自己的基本问题当然是是我在逻辑上建模值或事物引用?这就是为什么值类型和引用类型首先被称为“值类型”和“引用类型”。
您是否打算将“卡片”作为参考处理,就像处理具有身份的物理对象一样?例如,假设您正在为Canasta建模,它同时使用两个标准卡片组进行播放。你是否想要跟踪两个不同的黑桃皇后并将它们视为参考不同的?
或者您打算将它们视为值,就像处理数字一样?你永远不会说“这里的六号与那里的六号不同”,因为数字只能通过它们的数值来区分。
答案 1 :(得分:5)
为卡片设置struct
或class
,卡片的值应为enum
,卡片的套装也为enum
。
要制作套牌,您可以使用class
,它包含card
的列表,有Shuffle
等操作。
答案 2 :(得分:2)
您可以将每张卡片表示为一个数字,然后将它们包装在枚举和类中。如果介于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)和课程。这个类只不过是数字的包装器。您可以根据需要向类添加任何操作,方法和属性。当您存储或传输数据时,您只需使用数字。
答案 3 :(得分:2)
我不相信你真的在这里问正确的问题。如何代表您的卡片应该取决于您想要对卡片做什么;通过回答现在如何代表您的卡片的问题,您将自动约束自己可以解决其他更有趣/更具挑战性的问题,这些问题将在以后遇到。
您真正应该考虑的事情是您将如何实施计划的行为。例如
发出“卡”后会发生什么?您想表示在持有这些卡的实体之间实际移动的卡吗? (例如,甲板,玩家的手,弃牌堆,桌子等),或者您是否希望这些实体仅仅引用卡的中央存储库?或者可能使用完全独立的计数机制?
你打算如何“订购”卡片?你选择实施的游戏可能有不同的规则(例如Ace低或高?皇家套装都有相同的价值吗?会有王牌吗?)这些可能是决定是逐游戏决定的,甚至在游戏过程中也发生了变化,因此根据它的排序简单地定义一张卡片可能不是一个好的抽象。
您尝试表示哪种行为和游戏规则取决于卡?卡本身是否有任何依赖?
卡片是否会参与用户互动,还是纯粹是程序中定义纸牌游戏的部分所使用的逻辑对象?
您是否考虑过使用不同类型卡片的可能性?您在原始问题中提到了一个类,但如果一张卡要定义任何类型的状态,行为或与其他类交互,那么您可能首先要考虑在担心细节之前定义一个Card接口。
作为对你问题的直接回答 - 我个人并不认为你已经提供了足够的答案信息,而不是基于猜测和个人偏好的意见;这没关系,因为通常很难在你开始实施之前提出所有的答案。您可能想尝试所有三种,看看哪种最适合您的需求。不要通过事先做出这些决定来约束你的设计。
答案 4 :(得分:0)
您可能会考虑要尝试构建的抽象。如果一张卡片是其他功能传递的低级对象,那么更简单的enum
可能更合适,但是如果您希望卡片能够draw()
本身或其他任何你需要的卡片更复杂的东西。卡片如何适合您的应用程序?
答案 5 :(得分:0)
我认为这个问题在逻辑上可以得到解答。我将从面向对象的角度来看一下它。我认为一张牌有两个子元素,套装和价值。那些应该是enum
。手(类)应该包含一张卡片列表,这使我相信最好将卡片作为结构或类。为了确定一只手是否有一对,这将是手类的一个功能。
由于有多个人玩不同类型游戏的游戏,每个牌组都必须知道它有哪种类型的牌以及牌数。但即使在这种情况下,卡本身也是不可改变的。
答案 6 :(得分:0)
我会使用枚举的类或结构,并在其上放置一个内部构造函数:
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)。