source: https://www.thedrinksbusiness.com/

「大吃一斤」 — 談單位的計算與表現

--

前陣子筆者分享了在 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

--

--