字典设计

字典设计

修改时间

文章可能会随时修改,慢慢完善。
2018-11-25 15:00

前言

游戏服务器在启动时,需要加载某些静态数据到内存之中,比如所有商品的配置数据(可能包含id、名称、价格等属性)。
这些静态数据在被加载后,会在内存中以某种形式呈现、组织和使用,在这里假设称它为"信息"。

见过很多种关于这种"信息"的命名,比如配置信息(config)、模型数据(model)、模板数据(template)、定义数据(def)。
在这些年中经历了以上的命名,最后还是选择了字典条目(dict)作为这种"信息"的命名。

数据载体

承载这些静态数据的载体有很多种,在选择上可能会基于开发语言或是团队喜好(方便、直观等)。

  1. 基于代码的方式,将数据组织成语言代码,比如lua、json、pike等。
  2. 基于excel,直接使用excel来保存数据。
  3. 基于数据库表。
  4. 基于文本文件,比如csv、lua、json等。

术语

entry:字典条目,一般来说,一个字典条目对应一条记录。
dict:字典,某一类型的所有字典条目组成了一部字典,因此系统中很有可能会存在多部字典。

加载过程

当在系统中使用字典条目时,我们希望能直接获取和使用,不需要自己再做二次解析、转换等繁琐步骤。
为了达到以上目的,一般来说我们会将字典的加载过程分为两个步骤:

  1. 读取数据,可以认为是字典条目的处理。
  2. 组织、校验,可以认为是字典的内部结构组织和校验。

一、读取数据

  1. 从数据载体中读取数据记录,并将每条记录转换成对应的字典条目。
  2. 对记录中的特殊字段做解析,比如某些字符串其实是数组、键值对、枚举等。也就是说,字典条目中的字段完全是存放解析之后的数据,没有存放临时数据的字段。
  3. 对字典条目中的某些字段做校验,比如最小值、最大值、非空等。
  4. 对记录中某些字段的解析,是有先后顺序的,比如有些字段的值依赖于其它字段的值。
  5. 以上步骤应该都是自动完成的。

二、组织、校验

  1. 对字典的内部结构进行组织。字典的内部结构可能是map(可能有顺序),也可能是array或是table,或者是其它结构。使用这些结构是为了更方便的获取字典条目。
  2. 定义特殊的获取字典条目的方法。比如我想直接获取前三名的奖励配置,只需要调用这个特殊方法就可以取到,不需要取出这三个字典条目,然后分别读奖励配置字段。
  3. 字典与字典之间可能是有依赖关系的,可能会使用其它字典条目。
  4. 字典整体的校验、处理。
  5. 以上步骤会有些部分重合。

FAQ

Q: 字典是放在各个模块中,还是统一存放在字典模块中?
A: 目前倾向放到字典模块中。字典并不涉及到特定业务逻辑,并且一个模块可能需要使用不同功能的字典数据。

Q: 使用哪种数据载体存放字典文件比较合适?
A: 这个还真得根据团队来看,目前在游戏服比较喜欢使用文本格式,查看比较直观,容易追踪版本变化,缺点是可能得写一套专门的导出工具。

Q: 怎么校验字典条目中某些字段的值?
A: 自己手写代码检查太费劲,可以使用注解(比如javax.validation),统一校验。

代码片段

仅仅用作参考演示。

/**
 * 数据字典基类。
 */
public abstract class Dict<T extends Entry> {
	/** 存放原始字典数据条目,不要直接使用它。 */
	protected TreeMap<Integer, T> primitiveEntries;

	/** 赋值处理,将数据中的每个值赋值给字典条目中的字段。 */
	private Map<Integer, Assignment> assignments;

	/** 生成字典条目实例。 */
	public T newEntry() { ... }

	/** 待解析的数据字典文件。 */
	public abstract String getFilenames();
	
	/** 加载数据。 */
	public boolean load() { ... }

	/** 校验、组织。 */
	public abstract boolean process()
	...
}

/**
 * 数据字典条目基类。
 */
public abstract class Entry {
	/** 唯一标识。 */
	public int id;

	/** 有些列需要先被处理。 */
	public String[] getFirstColumns() { ... }

	/** 有些列需要后被处理。 */
	public String[] getLastColumns() { ... }
}

=======以下是具体的字典例子=======。

/**
 * 通过使用注解来定义字段校验。
 */
public class BattlePositionEntry extends Entry {
	/** 站位1。 */
	@Min(value = 1)
	public int num1;

	/** 站位2。 */
	public int num2;
}

/**
 * 这里存放字典条目的内部数据结构并不是map,而是一个数组。
 */
public class BattlePositionDict extends Dict<BattlePositionEntry> {
	private volatile int[][] positions;
	public boolean process() { ... }
	public int[][] getPositions() { ... }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章