「大吃一斤」 — 談單位的計算與表現
前陣子筆者分享了在 Naming Things 一書中的一段,作者建議我們要表現「量測值」時,附上單位會比較好。
請問你多重?
什麼意思?例如你有個 User 物件,有個方法名為 getWeight()。這個方法顧名思義就是要拿體重的對吧?假設你呼叫後得到一個數字為 100,你就知道這個 user 體重為 100公斤,對吧?
不對喔!他有沒有可能是 100 英磅?有可能呀!如果一個方法回傳的值是一個量測值,那麼作者建議我們在名字上加個「單位」,就不會搞混了,例如 getWeightInKg(),或是 getWeightInLbs()。
當情況有變
一般來說,我們的量測單位要視我們服務的對象的習慣而定。這套軟體如果是台灣人要用,那就用 kg,如果是美國人要用,那就用 lbs。但當情況有變,我要同時賣給兩個國家的用戶,或是,我就是有時需要 lbs,有時需要 kb 呢?
「這有什麼好難的,就開兩個接口呀!」
聽起來超合理對吧?user 身上存個以 g 為單位的 field,開兩個接口,要 kg 時除以 1,000,要 lbs 時除以 453,對吧?的確不難。
但是,這個類別就永遠封閉不了了。因為世上重量單位何其多?如果要賣去中國,還得加個中國斤之類的,加不勝加。
不只體重,物品也有重量。如果用這個方法,那麼桌子也要開三個接口,椅子也要、小狗小貓都要開三個接口,而且從此以後,每多支援一種單位,所有物件都要加開一個長得幾乎一樣的接口,非常麻煩!
怎麼辦?
VO 來了
那就這樣吧!我們定義一個名為 Weight 的物件存在 User 身上,也就是:
Weight weight = user.getWeight();
long kg = weight.getKg();
long lbs = weight.getLbs();
把基本型別、看不出單位的數字封裝起來,用語意更好的 Weight 物件來表示。在系統中每個有重量的物體,身上都有一個 Weight,這下要再多支援一種重量單位,只要幫 Weight 多加接口就好,基他地方都不用動。這下看起來專業多了吧?
好還要更好
有了 Weight 物件雖然每次要新支援一種重量單位只要改一個地方,但還沒解決 Weight 接口數無限長大的問的。我就問,以後每次要新支援一種重量單位,有沒有辦法不要「修改」Weight 物件,而只要「新增」一些東西?
有啊!
Martin Fowler 的 PoEAA 一書中,在「貨幣」的換匯中有類似問題。我們改一下他的 Solution 來套看看我們的場景吧!
假設 Weight 有個 get(Unit) 的接口,參數是單位(可能是 enum),而 enum 自己知道自己跟公克的換算比例。這時,上面的程式就可以寫成:
Weight weight = user.getWeight();
long kg = weight.get(Unit.KG);
long lbs = weight.get(Unit.LBS);
這下要加入中國斤或是台斤,也就是加上新的 enum 就好:
long ckg = weight.get(Unit.CKG);
long tkg= weight.get(Unit.TKG);
是不是很方便?
Context 為王
以上我們介紹了三種表示不同單位重量的方法,當然,長度、力量 … 等測量值都可以如法炮製。只是,是不是一定要用 PoEAA 的方法才行,筆者認為倒也未必,有些系統就是比較簡單,你硬是套一個複雜的 Solution 也未必有好處,反而徒增複雜度。
Context 為王,還是要看你個系統現在適合什麼,佐以足夠信心的單元測試,就不怕以後需要改了。
More about Me
- 更多軟體工程相關討論:https://www.facebook.com/kukumamaya
- 我的YouTube 頻道:https://www.youtube.com/@yu-songsyu6598
- 沒時間寫測試?沒時間重構?你就是不寫測試才會沒時間: https://www.tenlong.com.tw/products/9786263332645?list_name=lv