軟件不軟的一個主要原因,是其經(jīng)常處于變化之中。所以茫因,當(dāng)策略分離不同的變化方向被提出后,一個隨之而來的問題也就產(chǎn)生了:何時分離杖剪?
盡管一個軟件已經(jīng)滿足了當(dāng)前所有需求冻押,作為富有經(jīng)驗,傷痕累累的程序員盛嘿,我們卻毫無喜悅洛巢,因為我們很清楚,事情還遠未結(jié)束:用不了多久次兆,新需求就會再次排山倒海般涌來稿茉。而當(dāng)前設(shè)計能否頂?shù)米∠乱徊ǖ臎_擊,沒有人心里有底。
懷著不安的心情漓库,我們打開IDE恃慧,調(diào)出代碼,翻頁跳轉(zhuǎn)米苹,試圖從字里行間揣測未來變化的可能性糕伐。可深究起來蘸嘶,幾乎每一行代碼良瞧,每一項知識都似乎存在變化的可能性。但如果把每一種可能的變化方向都進行分離的話训唱,系統(tǒng)又會陷入無邊無際的復(fù)雜性褥蚯。這種兩難的境地讓我們感到絕望,強大的無力感讓我們一度懷疑自己是否選錯了行况增。
一段時間之后赞庶,大批新需求終于來了。經(jīng)過一番分析澳骤,變化的方向逐漸清晰明了歧强。但交付期限所帶來的急迫感和焦慮感,讓我們覺得自己根本沒有足夠的時間深入思考如何對原有設(shè)計進行合理的修改以應(yīng)對這些變化为肮,而對于修改已有代碼所可能帶來的潛在風(fēng)險摊册,也在不斷提醒我們應(yīng)該“保守穩(wěn)健”。于是颊艳,我們再次祭出殺手锏——copy-paste-modify 三步曲——這不僅可以讓我們看似很快的完成交付茅特,還可以讓管理層看到:我們又在短時間內(nèi)產(chǎn)出了如此巨量的代碼,從而認為我們有著驚人的生產(chǎn)力棋枕。至于帶來的可維護性問題白修,呵,堆自己的代碼重斑,讓別人去重構(gòu)吧……
這兩種傾向在實際項目中都并不罕見兵睛。前者經(jīng)常造成設(shè)計師徒勞無功的預(yù)測未來,其過度設(shè)計的傾向給系統(tǒng)帶來諸多不必要的復(fù)雜度窥浪;后者則快速讓一個系統(tǒng)陷入步履維艱的“焦油坑”卤恳。
與此有關(guān)的另外一個例子是,當(dāng)年第一次讀到單一職責(zé)原則時寒矿,對其含義一個類只應(yīng)該有一個引起它變化的原因大惑不解:這個類未來會以怎樣的方式變化我是無法預(yù)知的突琳,我怎能在當(dāng)下就判定一個類是否符合單一職責(zé)?
多年之后符相,我才意識到拆融,對于這個問題的答案其實已經(jīng)給出蠢琳,只是隱藏于別處:當(dāng)談到何時應(yīng)用開放封閉原則時,Uncle Bob給出的答案是:被第一顆子彈擊中時镜豹。
這是一個絕妙的隱喻傲须。
它首先強調(diào)了時機:不早不晚,第一顆趟脂。早了泰讽,過度設(shè)計;晚了昔期,則成為再次被愚弄的傻瓜已卸。
其次,它還隱含著變化的方向:當(dāng)?shù)谝活w從某個方向射來的子彈擊中你后硼一,你就必須首先解決來自那個方向的威脅累澡,盡管子彈還可能從任何其它方向射來,但那是另外一個問題般贼。所以愧哟,開放封閉原則不可能對所有的擴展都開放,對所有的修改都封閉哼蛆。只有每次當(dāng)你被來自某個方向的第一顆子彈擊中后蕊梧,你及時運用了此原則,筑起針對此方向的防御工事腮介,隨后望几,你才可以對來自此方向的其它子彈做到“開放封閉”。
換一個說法萤厅,這個世界里,本質(zhì)上只存在三個數(shù)字:0靴迫,1惕味,和N。
0意味著當(dāng)一個需求還沒有出現(xiàn)時玉锌,我們就不應(yīng)該在系統(tǒng)中編寫一行針對它的代碼名挥。
1意味著某種需求已經(jīng)出現(xiàn),我們只需要使用最簡單的手段來實現(xiàn)它主守,無需考慮任何變化禀倔。
N則意味著,需求開始在某個方向開始變化参淫,其次數(shù)可能是2救湖,3,... N涎才。但不管最終的次數(shù)是多少鞋既,你總應(yīng)該在由1變?yōu)?時就需要解決此方向的變化力九。隨后,無論最終N為何值邑闺,你都可以穩(wěn)坐釣魚臺跌前,通過一個個擴展來滿足需求。而這正是我們苦苦追求的正交性陡舅。
0抵乓,1,N就是簡單設(shè)計的精髓靶衍。
從而灾炭,就不難理解:所謂單一職責(zé),就是當(dāng)變化還沒有發(fā)生時摊灭,我們可以認為任何一個類都是符合此原則(除非特別明顯的職責(zé)耦合)咆贬。當(dāng)變化初次發(fā)生時,將一個個變化的原因分離出去帚呼,然后掏缎,得到了一組類,它們依然符合單一職責(zé)煤杀。
變化與職責(zé)眷蜈,共生共滅。
最后需要強調(diào)的是:在被第一顆子彈擊中時沈自,能否做出正確的應(yīng)對酌儒,除了辨別和解決問題所需的敏銳嗅覺之外,還需要勇氣枯途。當(dāng)有人問我為何總是敢于在遺留系統(tǒng)上大刀闊斧時忌怎,答案很簡單—— 膽大心細。