在我和同事結對時,發現數據庫中多個表中,分別都會有gender這個字段。好比A表,B表,C表。這三個表中,gender字段都是int類型。可是同一性別,在各個表中的值是不一樣的。好比A表中,1表明男,在B表中卻表明了女,在C表中表明未知。html
我忽然意識到這背後存在更大的問題。從而引起我對「性別字段存儲時應該使用的字符串,仍是數字?」這個問題思考。也許已經有前輩思考過這個問題並寫在某本書的某頁,若是有,請告知。謝謝。前端
0表明女,1表明男java
首先,你可能會問,對於這樣的問題還用想嗎?不是都使用數字嗎?0表明女,1表明男。程序員
其實,淘寶就是這麼作的:數據庫
html代碼是這樣的:json
這時,我會問若是這個用戶沒有填寫性別信息呢?那你可能將原來的實現改爲0表明空,1表明男,2表明女。我提醒你,當你開發的是一個大型網站時,你要將原來的「0表明女」改爲「0表明空」,不會那麼容易。歷史數據要處理。你還須要修改全部用到0,1的代碼,即便你使用的是常量代替而不是魔法數字,也不會容易到哪裏去。後端
有經驗的程序員設計模式
是的,有經驗的程序員寫代碼時,一開始就會想到這個問題,因此一開始就設計「0表明空,1表明男,2表明女」。從前端到後端都統一使用數字。好比:架構
class User{ final static int GENDER_NULL = 0 final static int GENDER_MALE = 1 final static int GENDER_FEMALE = 2 int gender } class UserController{ void saveUser(int gender) } <div>gender: #if($user.gender == 1)男#elseif($user.gender == 2)女 blabla….</div>
固然前端這樣寫有些難看,那咱們使用宏來代替,好比<div>#displayGender($user.gender)</div>。這裏我想留一個疑問:若是想國際化呢?你的displayGender怎麼實現的?框架
實習生來了
某天公司招來了一個實習生要實現一個活動申請表頁面。領導以爲這個功能應該不難,因此就將這個任務分配給他。他爲了表現本身,哐啷哐啷很快就寫完了,還獲得了領導的表揚。但實習生根本沒有參照前面有經驗的程序員的寫法(有時不是他的問題,多是沒有人告訴他須要參照某個功能的寫法來實現)。有意識一些的實習生還知道將gender的值寫成常量,沒有意識的,可能你只有去到前端頁面看源碼才能知道0, 1分別表明什麼。
class ActivityApply{ final static int GENDER_MALE = 0 final static int GENDER_FEMALE = 1 int gender }
若是他沒有參照前面有經驗程序員的寫法,我不肯定他是否會重用那個前端宏。因此,講到這裏,你應該明白,有時你設計好的「重用」,並不必定會被重用。爲何呢?:P
這裏不是故意貶低全部實習生,只是情節須要。
0和1到底放在哪裏?
也許你意識到了(一般不包括架構師),咱們須要統一將gender常量的值放在某個地方。那位有經驗的程序員將其放到了User類中。這樣,全部使用的gender的地方都應該變成User.GENDER_MALE blabla,如 activityApply.gender = User.GENDER_MALE。
也許有人想到了,創建一個Gender的類,或者枚舉不就好了。好比:
public enum Gender { UNKNOWN(0), MALE(1), FEMALE(2); private final int value; Gender(int value) { this.value = value; } public int getValue() { return value; } }
而後使用的時候就變成了:
user.gender = Gender.UNKNOWN activityApply.gender = Gender.MALE
問題是否是解決了?就算是實習生來了,也能保證你們的gender的值是一致的。前提是他要知道關於gender的值咱們取的都Gender枚舉裏的值。不管是入職時老員工跟他說,仍是他本身發現的。
問題解決了?並無。當前端發來了個gender參數時,咱們如何校驗這個參數呢?好比前文提到的淘寶表單裏,咱們看到:
_fm._0.g就是gender參數。
校驗時,咱們的controller裏,有人可能會寫成:
if(gender == 2){ user.gender = Gender.FEMALE }else if(gender == 1){ user.gender = Gender.MALE }else { user.gender = Gender.UNKNOWN }
高明一些人的會在gender枚舉中加入一個靜態方法:
public static Gender genderOf(int aGenderValue){ for (Gender gender : Gender.values()) { if (gender.value == aGenderValue) { return gender; } } return Gender.UNKNOWN; }
而後校驗時,
user.gender = Gender.genderOf(genderParameter).value
Gender以數字值存到數據庫中,真是最好的方法?
以上,咱們的思路看似沒有問題。只是,咱們沒有看到其中的假設。以上思路的假設是:
代碼使用者知道有Gender這個枚舉類,而後再使用Gender枚舉賦值給User.gender字段。
對於數據庫中的0、一、2,只有咱們的程序進行解釋,其它程序裏可能使用的是十、十一、12
由於是性別可能的值很少,因此,前端代碼寫成if elseif elseif else,沒所謂。但要知道,咱們服務不只僅輸出html,還會輸出json等其它格式。固然,你能夠將這部分邏輯封裝起來,這樣別人就能夠重用了。可是你這裏又假設了「TA人知道你的重用」存在,而後正確使用。
首先,你可能認爲這些不是問題。我猜測你給出的理由是:
關於1,一進入公司,咱們就培訓他,gender就使用這個類。或者寫一個開發文檔。
關於2,咱們不須要其它程序解釋這個數據庫裏的值,其餘程序都是經過調用咱們程序的。
關於3,性別來來回回就那幾個,不會擴展到哪裏去。
問題不在於Gender而在於別處
我以爲你這些理由是有道理的,可是不是最好的。
關於1:咱們的代碼應該設計得儘可能可靠,可靠到連代碼使用者都不會使用錯。而User、ActivityApply的gender是int和String這類基礎數據類型,無論你培訓仍是寫開發文檔,都會給代碼使用者有寫錯的機會。更好的辦法是什麼,使用枚舉類型。這樣就能夠由編譯器來給咱們檢查代碼使用者有沒有調用錯了。並且,這也解放了代碼使用者的大腦。當你在寫user.setGender(value)時,若是setGender接收的是一個枚舉,IDE天然會提示你,gender有哪些值。
這很像地鐵上的門寫着「請勿倚靠」,你就認爲真沒有人倚靠?在合理成本內,地鐵門應該設計成就算10個200斤的人倚靠都不怕。
這時,你會提問,若是User gender使用的枚舉,那麼咱們怎麼持久化到數據庫中呢?若是使用ORM框架,它會給你解決。若是不使用ORM,也能夠有技巧解決的。這個問題留你本身思考。可是,有一點須要提下,你的業務代碼不該該和數據庫這樣具體技術耦合!能夠看看我寫的《耦合的本質》
關於2:若是養成了全部有限值內的字段都使用數字來存到數據庫中的習慣,問題就沒有使用gender這麼簡單了。在將來的幾年,你的代碼會充滿魔法數字,最終架構腐化。
關於3:和2是同一個問題。
小結
回答本文標題的問題:出現像gender這樣有限值的字段,我會優先使用枚舉包裝起來,持久化時,我會優先使用人看得懂的字符串。
深度一些的思考:
本文的標題是個問題嗎?
設計模式能解決本文標題的問題嗎?呵呵
爲何人們趨向於使用數字而不是字符串?
爲何架構會腐化?
架構師不寫代碼?
若是以爲這文章對你有幫助,能夠讚揚10元: