Truly Type Safe

今天受 poyenc 開示關於物件的抽象化以至於真正的 type safe ,撰文以誌之。

從一個簡單的結構開始。今天要你實作一個關於「人」的數據資料,大部分最直觀,「表面上」最省力的地方是簡單的如下宣告:

struct Person {
  std::string name;
  std::string id;
  int age;
};

但要回頭想一想難道這真的表達了我們想要的資料結構嗎?對名字、身分證字號、年紀這三者用以上的變數宣告都太攏統,也不安全。如果要把這個結構導入系統中,會有許多不符合使用情境卻被合法的裝進 struct Person 結構中的情況發生。

不合法情況像是:

  • id 身分證字號應該由 1 個英文字與 9 個數字組成,其餘為不合法身分證字號
  • age 不應該出現負數

對一個初次閱讀這段程式碼的人,更不會知道該如何使用這個結構,怎麼樣才是合法的程式碼。

有了 member type 這種事情,可以讓使用 struct Person 的人遠離變數型態的煩惱。他不需要知道在 *_type 底下是什麼樣的結構。他可以根據他對 name , id , age 這些型態的認知來操縱這個變數。這樣僅做到抽象化的動作,他跟上面的程式碼片段別無二致,仍然是不安全的。

struct Person {
// member type
  using name_type = std::string;
  using id_type = std::string;
  using age_type = int;
// variable declaration
  name_type name;
  id_type id;
  age_type age;
};

要做到「真正的」type safe ,我們就需要把問題拿到手上並動手解決。對型態都要有明確定義, 從 constructor, destructor, copy operator, move operator 等等⋯⋯都要自己親自去 overload 。當然會比較費工,但是要做到這個地步才能確保他完全安全

class Name : private std::string {
// 台灣人名格式
};
class taiwanId : private std::string {
// 台灣身分證格式
// - 限制只能有 1 個英文字元
// - 英文字元後接 9 個數字
};
class Age : private int {
// 年紀 >=0
};
struct Person {
  using name_type = Name;
  using id_type = taiwanId;
  using age_type = Age;
  
  name_type name;
  id_type id;
  age_type age;
};

可能你還好奇為什麼要用 using 來把這些 type 再包一層,因為這又是一層抽象化!今天假設你想要換成美國的 social security number ,這時 ID 的格式就改變。但好險你在 struct Person 底下對 ID 的操作都是根據 id_type 來做,所以只要更改 id_type 等號右邊的結構,就可以換成美國人的身分證結構了,增加了程式碼的可攜性

struct Person {
  using name_type = Name;
  using id_type = usaId; // 只需要改這裡
  using age_type = Age;
  
  name_type name;
  id_type id;
  age_type age;
};

總結以上有兩個重點:

  • 真正的 type safe 要解決隱含的資料結構問題就需要繼承到自己手上解決
  • member type 可以增加程式碼的可攜性

BTW 寫出優質的程式碼這檔事大學沒教真的是太可惜了!

如果你覺得有收穫,可以用 30 NTD 來支持我繼續創作更多內容。因為做自己喜歡的事而得到報酬,是再好不過的事了。(街口支付)

Author: eopXD

Hi 我是 eop ,希望人生過得有趣有挑戰XD

Leave a Reply