Hi 我是 eop 。 畢業於台灣大學資訊工程系(NTU CSIE), 現在是 Sifive 的 Compiler Engineer。 (Ex-Skymizer Compiler Engineer) 你可以在 eopxd.com 找到我! 我會發布一些對技術的整理、閱讀或觀影的心得或是生活的感悟。 如果覺得這些創作有價值,歡迎支持我。 希望人生能一直過得有趣又有挑戰。
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 寫出優質的程式碼這檔事大學沒教真的是太可惜了!
Original link: eopXD
喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。
发布评论…