字典設計

字典設計

修改時間

文章可能會隨時修改,慢慢完善。
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() { ... }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章