12

我一直在开发一些手机游戏,这些游戏从服务器数据库中获取数据。

我习惯于将“类型”值存储为整数标识符,并在客户端中使用枚举来标识来自服务器的数据。

例如:在数据库表上:

怪物表:MonsterId (int), Name(string), MonsterType(int)

在客户端代码上:

typedef enum {
    MonsterTypeGround = 1,
    MonsterTypeAquatic = 2,
    MonsterTypeAmphibious = 3,
    MonsterTypeAerial = 4
}MonsterType;

请注意,上面的代码是 Objective-C,我可以在其中分配整数值。但我也在使用 C# 和 C++。

但是,我们的数据库人员说枚举是一种编译器技巧,没有枚举数据类型之类的东西。他认为整数类型标识符使人们(其他开发人员)难以理解值的含义,并且如果不查看客户端代码,他们不可能知道等价物,并且枚举不好,因为您需要确保枚举与服务器端 id 同步,并且最好使用字符串。

我的问题是:关于此事是否有客观正确的答案?

除了在客户端代码上使用枚举但仍将整数用于服务器数据库之外,还有其他选择吗?

4

2 回答 2

15

长答案

您的数据库人员显然是错误的。当然,如果有类似 ENUM 数据类型的东西。您在示例中提供了一个。MySQL 知道这一点。许多编程语言都有类似 ENUM 的东西。

但他也是对的,因为 ENUM 通常(总是?)由编译器优化。如果有四个选项可以用 1 到 4 完美表示,但我们碰巧发现可识别的字符串在代码中更容易阅读。但是编译器没有这样的问题,实际上并不关心数字。Aerial Monster 是 4 类,Flying Spaghetti Monster 是 4 类。相比比较字符串,CPU 比较字节甚至更容易几个数量级。

此外,他认为在 C 代码或任何代码中使用 ENUM 可能是一个问题是正确的:

  1. 如果更改定义(尤其是顺序),则需要重新编译程序和所有链接的程序(如果它是库)。那是一种痛苦。
  2. 如果您需要使用不同的语言进行交互,那也可能会很痛苦。您需要同步多个定义。
  3. 管理 ENUM 很麻烦,尤其是删除类型。

您可以通过使用将字符串转换为枚举或其他方式的函数来解决此问题。

但也有好处:

  1. 如果您更改怪物类型的名称,任何空中怪物都可以保持类型 4,即使您将它们重命名为飞行怪物。保证数据库中的数据一致性,因为它是一个数字。如果您使用字符串,则没有轻松的转换方式。好吧,查找和替换将在代码中执行,但在数据库中则不行。
  2. 这是一种有效的格式。它将为您节省 10 个字节。我的经验是,这很少有关系,除非你有数千万个条目。

TL;博士

不,没有客观的答案。

如果您发现程序员的易用性最重要,那么字符串可能是更好的选择。如果您发现编译器优化最重要,那么枚举是更好的选择。

我的观点是编译器优化很少重要,但程序员的时间很少。我自己主要使用字符串,除了在某些数据库中。

所以,是的,你的家伙有一点。

于 2013-02-15T11:05:27.517 回答
5

好吧,这里开始......潜在的downVote素材,但枚举是我最喜欢的东西之一......

看法

专注于最适合您的设计和代码的内容。不使用枚举是因为数据库没有那种“数据类型”?好的。那么我们不要出于同样的原因制作任何自定义类。那是胡说八道。

枚举善良

(注意:我用 C# 编写代码)

  1. 允许您根据问题域声明事物。字符串是字符串,但 MonsterType 枚举是 Monster(类型)。
  2. enum 本质上是强类型的。使用未定义的枚举值是编译错误。OTOH,字符串拼写错误是另一个调试会话。
  3. 我特别喜欢它的switch陈述。击败 theHellOutta 整数:switch(MonsterType)
  4. 枚举可以详尽地定义所有有效值
  5. 倾向于文档化
  6. 代码质量增强器 - 确保您必须重新编译才能添加新成员,但这是深思熟虑的,并且缺少相关的代码更改会爆炸(一件好事)相对于未在某处处理的新字符串值,表现为难以捉摸逻辑/处理错误,需要您进行调试会话。
  7. 默认为零,不为空。有趣的是,null 概念是如何把事情搞砸的。在它所属的数据库中保留“null”。
  8. 专业编码提示:始终明确定义默认成员。回复:MonsterType.Unknown = 0。您的奖励将是更容易编写、错误更少且更易于阅读的代码。您的维护程序员会感谢您的。

弦坏

  1. 默认值为空。这是一场围绕 null 和“空字符串”的持续战斗。我经常看到像这样愚蠢的、漏洞百出的代码:if(string.IsNullorEmpty(myString.Trim()) ...- 如果 myString 为空,您的程序会因运行时异常而崩溃。枚举不能发生。
  2. 字符串比较区分大小写
  3. 容易出现拼写错误
  4. “字符串”类型是通用的,绝不可以帮助您根据您的域表达您的问题
  5. 从数据库中获取的设置myString = null不是一个有值的变量,它甚至不是一个对象,但我们试着像对待它一样对待它!

整数混淆

if (MonsterType == 3)... 这是什么意思?纳夫说。

这是一个实践练习。在您的 IDE 中,单击“3”并要求它“查找定义”。如果你的 IDE 会说话,它会说“你为什么不定义你的问题域中的东西,而不是让我猜“3”是什么意思?

哦,我知道。我会在某处声明一堆常量……某处。我的天啊。让我们假设我们正在使用枚举!而且它比使用一有凝聚力的类型安全值更有趣!

null 让我抓狂

C# 有一个String.Empty静态属性是有原因的。它不会劫持有效值(例如空格)或 null 来表示实例化的字符串对象,该对象的值明确不是任何(有效)字符串。这与 null 不同。

从字面上看,null 意味着什么都没有。意思是“走开,没人在家”。例如,编码人员经常希望它表示“未知的怪物类型”,但是为了大声喊出明确定义这些概念(参见上面的 enum pro 提示)。恕我直言,像这样使用 null 意味着您的设计缺少一些东西。

null 是代码僵尸。它在四处走动,但什么都没有,死了,无论如何。如果你不小心,它会咬你!

您将体验到决定将“空”字符串存储为空格字符或 null 的无穷乐趣;

并且空格实际上是有效的字符串域值,而不是“无值”值;

并且那个 null 和“空字符串”实际上并不意味着同样的事情,当你需要它们的“自然”功能时,试图让它们产生问题;

以及从 string.empty 到 null 到空间的代码转换的不断大惊小怪,反之亦然,以适应手头的任务。随着时间的推移,您的代码将在这方面变得不一致。

现在听我说,以后相信我。

于 2013-02-20T23:41:11.757 回答