在“类型”值(数据库和类设计)中使用整数还是字符串?

12

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

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

例如: 在数据库表中:

怪物表: 怪物ID(int), 名字(string), 怪物类型(int)

在客户端代码中:

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

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

我们的数据库人员说枚举是编译器的技巧,没有枚举数据类型。他认为整数类型标识符使得其他开发者很难理解值的含义,而且他们不可能知道等价值而不查看客户端代码,因此枚举并不好用,因为你需要确保枚举与服务器端ID同步,并且最好使用字符串而不是枚举。

我的问题是:这个问题是否有客观正确的答案?

除了在客户端代码中使用枚举还要在服务器数据库中使用整数之外,是否有其他替代方案?

2个回答

16

长篇回答

你的数据库人员显然是错的。当然,有一种称为 ENUM 的数据类型。你在示例中提供了一个,MySQL 也知道它。许多编程语言都有类似 ENUM 的东西。

但他也是对的,ENUM 通常(总是?)由编译器进行优化。如果有四个选择可以通过 1 到 4 完美表示,但我们在代码中发现可识别的字符串更容易阅读。但编译器没有这样的问题,事实上并不关心数字。空中怪兽是类型 4,飞行意大利面怪兽也是类型 4。与比较字符串相比,CPU 比较字节甚至容易几个数量级。

此外,他也正确地指出,在 C 代码或其他代码中有 ENUM 可能会成为问题:

  1. 如果您更改了定义(特别是顺序),则需要重新编译程序和所有链接的程序(如果是库)。那是很麻烦的。
  2. 如果您需要与不同的语言进行交互,那也可能很麻烦。您需要同步多个定义。
  3. 管理 ENUMs 很麻烦,特别是删除类型。

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

但也有好处:

  1. 如果更改了怪物类型的名称,则任何空中怪兽都可以保持类型 4,即使将它们重命名为飞行怪物。因为它是一个数字,所以数据库中的数据一致性得到保证。如果使用字符串,则没有无痛转换的方法。嗯,在代码中查找和替换会做到这一点,但在数据库中不行。
  2. 它是一种高效的格式。它将为您节省 10 个字节。我的经验是,除非您有数千万个条目,否则这很少重要。

简短回答

没有客观答案。

如果您认为程序员易用性最重要,则字符串可能是更好的选择。如果您认为编译器优化最重要,则 ENUM 是更好的选择。

我的看法是编译器优化很少重要,但程序员的时间却很宝贵。我自己大部分情况下都使用字符串,除了某些数据库。

所以,是的,你的那位同事说得有道理。


感谢您的输入!关于程序员易用性的话题,我发现枚举更容易使用,因为我可以直接将它们转换为整数,并使用该参数调用方法,而不是字符串,其中我需要执行[string isEqualToString:]来检查该字符串所引用的类型。我错过了什么吗?在寻找折中方案时,我认为您提到的“拥有一个将字符串翻译成枚举或反之亦然的函数”是一个不错的选择。 - Wintermute
是的,那可能是一个考虑因素。但在 C 中(你提到了 Objective C?),你可以创建一个函数,它接受一个枚举类型,如果我没记错的话。很久很久以前就没有用 C 编程了。 - Allard Hoeve
是的,我知道可以编写可以接受枚举的函数。但我说的是获取服务器值,并将其解析为代码可读取的值(在这种情况下是枚举)。谢谢! - Wintermute

6
好的,开始翻译。这里可能会引起一些负面评价,但枚举是我最喜欢的东西之一。
角度
专注于设计和代码的最佳实践。因为数据库没有“数据类型”,而不使用枚举?好吧。那么出于同样的原因,我们也不要制作任何自定义类。这是无意义的。
枚举优点
(注意:我用的是C#)
1. 允许您根据问题域声明事物。字符串是字符串,但MonsterType枚举是一个怪物(类型)。 2. 枚举是强类型的。使用未定义的枚举值会导致编译错误。另一方面,字符串拼写错误则需要进行调试。 3. 我特别喜欢它用于switch语句。比整数要好得多:switch(MonsterType) 4. 枚举可以详尽地定义所有有效值。 5. 倾向于具有文件性。 6. 代码质量增强器-当然,您必须重新编译以添加新成员,但这是深思熟虑的,而错过了相关的代码更改将爆炸(一件好事),与在某个地方表现为难以捉摸的逻辑/处理错误的新字符串值不同,需要您进行调试会话。 7. 默认为零,而不是空值。有趣的是,空值概念可能会混淆事情。将“null”保留在它应该在的数据库中。 8. 专业编码提示:始终明确定义默认成员。关于MonsterType.Unknown = 0。您的奖励将是更易于编写的代码,不容易出错且更易于阅读。您的维护程序员会感谢您。
字符串糟糕
1. 默认值为null。反复切换空值和“空字符串”是一场持久的战斗。我经常看到像这样的愚蠢、错误的代码:if(string.IsNullorEmpty(myString.Trim()) ... - 如果myString为null,则您的程序会因运行时异常而崩溃。枚举不会发生这种情况。 2. 字符串比较区分大小写。 3. 容易出现拼写错误。 4. “字符串”类型是通用的,并不能帮助您根据问题域表达您的问题。 5. 设置myString = null,从数据库中获取的IS NOT具有值的变量,甚至不是一个对象,但我们试图将其视为它!
整数混淆 if (MonsterType == 3)...这是什么意思?说得够了。
这里有一个实际的练习。在您的IDE中,单击该“3”,然后要求它“查找定义”。如果您的IDE能说话,它会说:“为什么不根据您的问题域定义这些东西,而不是期望我猜测“3”是什么?”

哦,我知道了。我会在某个地方声明一大堆常量... 在某个地方。让我们假装我们正在使用枚举!这样比使用一个具有连贯的、类型安全的值更有趣!

null让我疯了

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

null意味着什么也没有,字面上就是“走开,没人在家”。编程人员经常希望它表示“未知的怪物类型”,但天哪,要显式地定义这样的概念(参见上面的枚举提示)。在我看来,像这样使用null意味着你的设计缺少某些东西。

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

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

以及空格实际上是一个有效的字符串域值,而不是“没有值”的值;

而且,null和“空字符串”真的不意味着相同的事情,当你需要它们的“自然”功能时,试图让它们变得如此会导致问题;

还有,在代码内不断地调整将string.empty转换为null、space及其反之以适应手头的任务。随着时间的推移,你的代码在这方面会变得不一致。

现在听我说并相信我以后会明白。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接