如何在 Scala 中模拟基于字符串的静态 ID 的枚举?
How to model an enum of String based static Ids in Scala?
假设我有一个参考数据 table roles
填充了用户可能被授予的所有角色。行相当 stable,这意味着很少有人向 table 添加新角色。此外还有一个 users
table 和一个 join-table users_roles
。事实上,roles
table 只需要通过向 users_roles
.
添加记录来授予用户一些预定义的角色
roles
table 很简单:
CREATE TABLE IF NOT EXISTS admin.roles (
id CHAR(16) PRIMARY KEY,
description VARCHAR(256) NOT NULL
);
以下示例描述了一个角色:
INSERT INTO admin.roles VALUES('CS_AGENT', 'A customer service agent');
显然,我的代码中某处需要可能的 id
值。这是一组字符串,但我想防止魔术字符串并使其更安全。
据我了解,有以下几种选择:
- 为每个角色 id 创建一个符号
- 创建一个扩展
String
的新类型 RoleId
并声明 vals
为了定义角色 ID 集,这些是我的选项:
- 使用
Enumeration
- 使用密封的 trait/sealed 对象并从中派生案例对象
我正在为我的持久层使用 JOOQ,如果我可以在我的查询中使用类型安全的 RoleId 而无需手动将其转换为字符串,反之亦然,那就太好了。
最好的解决方案是什么?
我不太确定我是否解决了你所有的问题,但这样的事情不是解决方案吗?
/** Represents a RoleId from the roles table. */
sealed trait RoleId {
def name: String
def description: String
override final def toString: String = name
}
object RoleId {
case object CS_AGENT extends RoleId {
override val name = "CS_AGENT"
override val description = "A customer service agent"
}
// Define all other roles...
/** All roles */
val allRoles: Set[RoleId] = Set(
CS_AGENT,
// All other roles...
)
/** Returns an RoleId given its name, if the name is not found this will return a None. */
def fromString(name: String): Option[RoleId] = name.toUpperCase match {
case "CS_AGENT" => Some(CS_AGENT)
// All other cases..
case _ => None
}
}
这完全是 typesafe 如果你需要去 to/from 一个字符串有 toString
和 fromString
方法。
这种方法的唯一 (大) 缺点是有很多样板代码,很容易搞砸 - 创建一个新的 RoleId
但不将其添加到Set
、名称或案例中的拼写错误等
解决此问题的另一种方法是使此文件由 SBT 从某种配置 自动生成(如果可以访问,甚至读取 SQL table构建环境),对于我的那部分 到另一个问题可能有帮助。
假设我有一个参考数据 table roles
填充了用户可能被授予的所有角色。行相当 stable,这意味着很少有人向 table 添加新角色。此外还有一个 users
table 和一个 join-table users_roles
。事实上,roles
table 只需要通过向 users_roles
.
roles
table 很简单:
CREATE TABLE IF NOT EXISTS admin.roles (
id CHAR(16) PRIMARY KEY,
description VARCHAR(256) NOT NULL
);
以下示例描述了一个角色:
INSERT INTO admin.roles VALUES('CS_AGENT', 'A customer service agent');
显然,我的代码中某处需要可能的 id
值。这是一组字符串,但我想防止魔术字符串并使其更安全。
据我了解,有以下几种选择:
- 为每个角色 id 创建一个符号
- 创建一个扩展
String
的新类型RoleId
并声明 vals
为了定义角色 ID 集,这些是我的选项:
- 使用
Enumeration
- 使用密封的 trait/sealed 对象并从中派生案例对象
我正在为我的持久层使用 JOOQ,如果我可以在我的查询中使用类型安全的 RoleId 而无需手动将其转换为字符串,反之亦然,那就太好了。
最好的解决方案是什么?
我不太确定我是否解决了你所有的问题,但这样的事情不是解决方案吗?
/** Represents a RoleId from the roles table. */
sealed trait RoleId {
def name: String
def description: String
override final def toString: String = name
}
object RoleId {
case object CS_AGENT extends RoleId {
override val name = "CS_AGENT"
override val description = "A customer service agent"
}
// Define all other roles...
/** All roles */
val allRoles: Set[RoleId] = Set(
CS_AGENT,
// All other roles...
)
/** Returns an RoleId given its name, if the name is not found this will return a None. */
def fromString(name: String): Option[RoleId] = name.toUpperCase match {
case "CS_AGENT" => Some(CS_AGENT)
// All other cases..
case _ => None
}
}
这完全是 typesafe 如果你需要去 to/from 一个字符串有 toString
和 fromString
方法。
这种方法的唯一 (大) 缺点是有很多样板代码,很容易搞砸 - 创建一个新的 RoleId
但不将其添加到Set
、名称或案例中的拼写错误等
解决此问题的另一种方法是使此文件由 SBT 从某种配置 自动生成(如果可以访问,甚至读取 SQL table构建环境),对于我的那部分