漫談 軟件設(shè)計(jì)的目標(biāo)和途徑
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
作者 | blackheart
來源 | urlify.cn/iyueiu
76套java從入門到精通實(shí)戰(zhàn)課程分享
記錄一下筆者關(guān)于軟件設(shè)計(jì)的一些相關(guān)認(rèn)知。在開始之前,先引入兩個(gè)概念目標(biāo)和途徑
這里可能會(huì)有些咬文嚼字,不過主要是為了區(qū)分主觀和客觀的一些細(xì)微差異)。
1 目標(biāo)和途徑
我們?cè)谧瞿骋患虑榈臅r(shí)候,總是會(huì)帶有一定的目的性的:比如說一日三餐,是為了給身體補(bǔ)充所需的能量。那么這三餐具體如何落實(shí)呢,則會(huì)有多種多樣的方式。比如你可以選擇吃碳水食物、蔬菜、肉類、牛奶或者蛋類等等;也可以選擇通過靜脈注射一些所需的葡萄糖或者蛋白質(zhì)。總之,能夠?yàn)樯眢w補(bǔ)充能量就可以了。
1.1 目標(biāo)
那么在上述的小例子中,我們的目的就是給身體補(bǔ)充能量,用以維持正常的生命活動(dòng)所需。當(dāng)然也可以說是我們的目標(biāo),不過目標(biāo)側(cè)重于過程,目的則更強(qiáng)調(diào)結(jié)果。
1.2 途徑
從上面的例子中可以看出有多種方式可以達(dá)成我們的上述目的。其中每一種方式都是一條達(dá)成目的的途徑,當(dāng)然我們?yōu)榱搜a(bǔ)充均衡的能量,通常會(huì)搭配組合幾種不同的食物,我把這個(gè)稱之為手段或者方法。手段和方法帶有一定的主觀性;而途徑則是在描述客觀的可供選擇的一種方式。
2 軟件的目的
在開始討論軟件設(shè)計(jì)之前先問自己一個(gè)最基本的問題:我們?yōu)槭裁葱枰浖?/span>
筆者認(rèn)為是為了解決現(xiàn)實(shí)中某個(gè)領(lǐng)域的相關(guān)問題而存在的。就好比最初的計(jì)算機(jī)是用來計(jì)算導(dǎo)彈的彈道的。生活中常用的QQ和微信是為了滿足人們的社交通信需求的,淘寶京東等是滿足了人們的買買買的需求。
所以,軟件存在的目的就是它能解決一些領(lǐng)域的相關(guān)問題,這是它存在的唯一理由。
比如在黑客帝國(guó)這部電影中,不再被使用的程序只有一個(gè)下場(chǎng),那就是被刪除掉。
3 軟件設(shè)計(jì)的目標(biāo)
假如一開始就有了軟件,其實(shí)要不要軟件設(shè)計(jì)都不重要了。但是問題在于軟件不是憑空產(chǎn)生的,不是從0到1沒有中間過程就直接得到了想要的軟件的。在軟件從0到1的過程,就是軟件設(shè)計(jì)的作用范圍(所以在這里我用軟件設(shè)計(jì)的目標(biāo)這個(gè)概念)。因?yàn)檐浖嬖诘哪康脑谟谒芙鉀Q一些領(lǐng)域的相關(guān)問題,那么首先對(duì)軟件的最低要求就是它能用,能用來解決問題。比如一個(gè)數(shù)學(xué)上的加減乘除計(jì)算器,最低最低的要求是你要能把結(jié)果算對(duì)吧。所以軟件設(shè)計(jì)的目標(biāo)是什么?筆者認(rèn)為就是控制這個(gè)從0到1的過程,避免其失控(一旦失控你可能就連最低最低的軟件的要求都達(dá)不到了)。
《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道》一書的副標(biāo)題也是這個(gè)含義。它的側(cè)重點(diǎn)在于如何利用面向?qū)ο蟮姆绞綉?yīng)對(duì)軟件本身的復(fù)雜性,從而避免其失控。
那么筆者對(duì)軟件設(shè)計(jì)的目標(biāo)的認(rèn)知就是:避免軟件的失控。為什么是目標(biāo)而不是目的呢?是因?yàn)檐浖O(shè)計(jì)在軟件的整個(gè)生命周期中都是存在著的,這是一個(gè)持續(xù)的過程,直到軟件不再被使用的那一天;而非只在剛開始設(shè)計(jì)一下,后續(xù)就一成不變了。
4 失控的根本原因
上面推導(dǎo)出軟件設(shè)計(jì)的目標(biāo)是避免軟件的失控。那么是什么東西導(dǎo)致的失控? 你面臨的業(yè)務(wù)太復(fù)雜?項(xiàng)目遺留的代碼太爛?團(tuán)隊(duì)成員水平參差不齊?工期太緊張導(dǎo)致你無暇做設(shè)計(jì)規(guī)劃?也許吧,這些或多或少都確實(shí)是已經(jīng)存在的事實(shí)。
業(yè)務(wù)太復(fù)雜難道是失控的原因嗎?回想一下軟件的目的是什么?解決一些領(lǐng)域的相關(guān)問題,那么我們可以讓業(yè)務(wù)的復(fù)雜性會(huì)消失或者降低嗎?答案是肯定的,不會(huì)!這里就有人要說你放屁。。。你敢說我們無法降低業(yè)務(wù)復(fù)雜性,打你噢。你就是打死我復(fù)雜性也不會(huì)降低的,,,復(fù)雜性是業(yè)務(wù)本身存在的客觀屬性,是不會(huì)以人的意志來改變的,除非你不做它了。就像你現(xiàn)在要在淘寶買一個(gè)手機(jī),你人在北京,賣方在廣州,無論你用什么快遞方式,從廣州到北京這段物理距離上的時(shí)間消耗是無法消除的。你說你比較著急,那好,賣方給你選擇空運(yùn),很快你就收到貨了。你說空運(yùn)這不是降低了快遞時(shí)間,和降低復(fù)雜性不是一樣的嗎?其實(shí)并不是,因?yàn)閺?fù)雜性指的是無論你用什么快遞方式,從廣州到北京這段物理距離上的時(shí)間消耗是無法消除的,指的是這個(gè)過程你無法消除。但是總覺得怪怪的對(duì)嗎?是的,看起來是怪怪的,明明我收到貨的時(shí)間縮短了,怎么復(fù)雜性沒有改變呢?所以這里就引申出另外一個(gè)概念:業(yè)務(wù)交互方式所帶來的影響。這個(gè)影響非常之大,但是往往被我們所忽略,比如你選擇購(gòu)買發(fā)貨地是北京的賣方了,是不是時(shí)間又進(jìn)一步大大縮短了?實(shí)際業(yè)務(wù)上也是這樣的,業(yè)務(wù)本身具備的復(fù)雜性,以及我們?cè)诎褬I(yè)務(wù)轉(zhuǎn)化為軟件后的交互方式所帶來的影響,業(yè)務(wù)本身的復(fù)雜性我們無法降低和消除,但是后者交互方式則是可以控制的,這也是軟件設(shè)計(jì)的一部分,所以其實(shí)上面我們選擇空運(yùn)是改變了這部分。就好比你是一個(gè)B/S的應(yīng)用軟件,你的用戶在瀏覽器中看到了Web頁(yè)面。這背后你的Web頁(yè)面從服務(wù)器到用戶瀏覽器的過程和瀏覽器渲染頁(yè)面的過程是無論如何也無法消除的,但是瀏覽器可以緩存它,當(dāng)你下次再打開這個(gè)頁(yè)面時(shí),它就可以省掉上述的交互過程。
項(xiàng)目遺留的代碼太爛是失控的原因嗎?其實(shí)也不是,這是失控的一種表現(xiàn)結(jié)果。
團(tuán)隊(duì)成員水平參差不齊是失控的原因嗎?也不是,這雖然是客觀存在的事實(shí),但是你這樣把責(zé)任推到隊(duì)友身上不合適吧,說不定隊(duì)友也是這么看你的呢。
工期太緊張導(dǎo)致你無暇做設(shè)計(jì)規(guī)劃是失控的原因嗎? 當(dāng)然也不是,這個(gè)是借口。。。就像你今天起床快要遲到了,你會(huì)選擇光屁股不穿衣服就出門嗎?
除了上述的一些事實(shí),當(dāng)然還有其他的一些因素,看起來都不像是導(dǎo)致失控的罪魁禍?zhǔn)?/strong>。那么究竟是什么導(dǎo)致的失控???仔細(xì)回想一下,當(dāng)我們覺得項(xiàng)目失控的時(shí)候通常是什么場(chǎng)景?
有個(gè)已知的bug,你改動(dòng)的時(shí)候發(fā)現(xiàn)牽扯的東西太多了,牽一發(fā)而動(dòng)全身,你不敢下手。你覺得代碼無法控制了。。。
有個(gè)未知的bug,你找了好久找不到,代碼太亂了。你覺得一股無力感。。。
有個(gè)新功能來了,你發(fā)現(xiàn)你要改這里那里,但是完全不知道改了會(huì)不會(huì)破壞現(xiàn)有的功能,也不知道新功能是不是真的可以work。你覺得你無法掌控這些代碼了。。。
還有一些其他的情況,總之就是你覺得你無法掌控代碼的真實(shí)行為了,你不知道你的代碼會(huì)產(chǎn)生什么樣的結(jié)果,就像薛定諤的代碼一樣。。。
那么還有一個(gè)場(chǎng)景,當(dāng)你要開展一個(gè)新的項(xiàng)目,所有的一切都是新的,沒有任何歷史債務(wù)負(fù)擔(dān),這時(shí)候你是什么感覺?信心滿滿啊肯定是,這時(shí)候你不會(huì)覺得你會(huì)對(duì)接下來的代碼失去控制,因?yàn)槟悻F(xiàn)在一行代碼都還沒有。。。
所以是什么導(dǎo)致的失控?現(xiàn)存的無力維護(hù)(bug、新功能都是維護(hù))的代碼導(dǎo)致的失控,同時(shí)這也是失控的表現(xiàn)結(jié)果。那么你為什么會(huì)無力維護(hù)這些代碼,因?yàn)樗恼鎸?shí)行為和你理解的行為出現(xiàn)了偏差,你覺得它不可控了。這時(shí)候就是真的失控了,代碼爛不爛其實(shí)并不是重點(diǎn),只要你還能維護(hù),這些都不是問題。
代碼只會(huì)按照你編寫的行為去執(zhí)行,而不是按照你認(rèn)為的行為去執(zhí)行。
那么如何避免失控?編寫可維護(hù)的代碼。打死你噢,解釋這么半天憋出這么一句廢話,誰不知道要編寫可維護(hù)的代碼啊。。。
我只能說別著急,繼續(xù)慢慢往下看。。。
5 目標(biāo)-可維護(hù)性
既然我們的目標(biāo)是避免失控,避免失控的途徑則是編寫可維護(hù)的代碼。那么我就把可維護(hù)性作為軟件設(shè)計(jì)的終極目標(biāo),而且沒有之一。也稱之為元原則,就是說我們目前所接觸到的各自編程原則、建議和最佳實(shí)踐等等都可以通過可維護(hù)性推導(dǎo)細(xì)化出來,并且不可與之相違背。
打個(gè)比喻,就好比憲法是其他一切法律的基礎(chǔ),任何法律如果違背了憲法,那么就是無效的。
那么根據(jù)可維護(hù)性可推導(dǎo)出來3個(gè)核心的原則:可理解性、可測(cè)試性和可隔離性。
5.1 可理解性
這條原則看起來很有主觀性的傾向,但是其實(shí)并不是。
比如說你剛寫了一段代碼,你覺得容易理解,他看起不容易理解;或者說代碼是他寫的,他看起來很容易理解,但是到你這里無法一下子理解他的思維,然后你就覺得不好理解。如果出現(xiàn)了這樣的情況,那么則統(tǒng)統(tǒng)都是不可理解的。這時(shí)候你要說了:你要一棍子打死雙方啊。是的,正是如此。再回想一下我們的目標(biāo)是什么?可維護(hù)性! 這里的維護(hù)不單單是說你的代碼你來維護(hù),而是大家互相交叉著;你新增了一個(gè)功能,后續(xù)負(fù)責(zé)其他的事情去了,那么這時(shí)候就由你的隊(duì)友來負(fù)責(zé)維護(hù)了;或者你接手維護(hù)別人的代碼。
所以我們需要一個(gè)客觀上的可理解性。那么到底什么才能叫客觀?沒法度量?。∑鋵?shí)也不復(fù)雜,就是看當(dāng)你讀到一段代碼的時(shí)候,你是否需要額外的思考,額外的腦中維持一個(gè)上下文的環(huán)境才能明白這段代碼的意圖,如果需要,那么就是不可理解的,至少也是不易理解的。更簡(jiǎn)單點(diǎn)說就是這段代碼應(yīng)該讓你不用思考就看的明白它的意圖。比如下面的一個(gè)小例子,功能是完全等價(jià)的,但是差異非常微妙。
// 1
if(userList.isNotEmpty()){
}
// 2
if(userList.isEmpty() == false){
}
// 3
if(!userList.isEmpty()){
}
// 4
if(userList.length() != 0){
}
你覺得可理解性怎么排?答案是肯定的吧?1 > 2 > 3 > 4。
1是不是你根本就不用思考,直接讀下來就知道其含義?
2則是有一個(gè)
==fasle的過程,需要你進(jìn)行簡(jiǎn)單的思考。3則是接近于2,但是比2更差一點(diǎn),因?yàn)槿》捶?hào)在前面,但是其決定性的值則在后面,而你的閱讀順序是從左向右,所以你需要一個(gè)比2稍微更復(fù)雜一點(diǎn)的思考過程。
前三個(gè)還都一眼能看出來是空或者非空的語境,但是4就更差了,4的字面意思是長(zhǎng)度不等于0,邏輯上其實(shí)和非空是等價(jià)的,但是你需要在腦中做這樣的一個(gè)映射長(zhǎng)度!=0等同于非空,這個(gè)的抽象層級(jí)明顯更低了一個(gè)層級(jí)。
不知道能否體會(huì)其中差細(xì)微差異。那么你覺得這些理解是客觀的還是主觀的呢?
5.2 可測(cè)試性
可理解性可以確保你可以快速的理解現(xiàn)存代碼的意圖,但是其真實(shí)的行為呢?是不是和你所認(rèn)為的行為就是一致的?上面我說過:“代碼只會(huì)按照你編寫的行為去執(zhí)行,而不是按照你認(rèn)為的行為去執(zhí)行”。
那么如何確保你真實(shí)的行為和你所認(rèn)為的行為是一致的?那就是測(cè)試。把你認(rèn)為的行為也寫成代碼,去驗(yàn)證你的業(yè)務(wù)代碼執(zhí)行的時(shí)候是不是會(huì)按照你給定的輸入得到你期望的輸出結(jié)果。借助自動(dòng)化的CI,就可以在你每次改動(dòng)代碼時(shí)把現(xiàn)有的所有測(cè)試都運(yùn)行一遍,然后你至少可以獲得3點(diǎn)收益:
代碼真的時(shí)按照你認(rèn)為的行為去執(zhí)行的。
確保你的改動(dòng)不會(huì)破壞現(xiàn)有的代碼行為。
倒逼你的代碼進(jìn)行合理的分解和抽象,不然你很難編寫有效的測(cè)試。
當(dāng)然你可能把測(cè)試寫錯(cuò)了,,,這種概率就小多了吧。況且假設(shè)你真的寫錯(cuò)了測(cè)試,時(shí)間久了,這個(gè)錯(cuò)誤也就變成了feature。為什么呢?也許你代碼的消費(fèi)方已經(jīng)按照它實(shí)際的行為去處理了,這時(shí)候你貿(mào)然把這個(gè)bug修復(fù)了,結(jié)果可能時(shí)消費(fèi)方反而不能正常工作了。這時(shí)候這個(gè)錯(cuò)誤的測(cè)試其實(shí)也就變成了消費(fèi)方的一種契約測(cè)試。確保你不會(huì)把它改對(duì),,,
比如C#的類庫(kù)中有個(gè)
DateTime,在處理時(shí)區(qū)問題時(shí)很多詭異的行為,這時(shí)候微軟已經(jīng)無法修正它了,只好再單獨(dú)新增了一個(gè)DateTimeOffset,兩者共存,慢慢的遷移過去。
5.3 可隔離性
那么現(xiàn)在你可以快速的理解現(xiàn)存的代碼了,也可以確保你的新代碼不會(huì)破壞已有的功能,也確認(rèn)你的代碼行為是你所認(rèn)為的行為了。是不是就可以愉快的合并代碼并且上線發(fā)布了?是的,差不多可以了。但是,凡是總有例外,我們不能把全部希望都寄托在我們能嚴(yán)格落實(shí)上述兩點(diǎn)。總是要有個(gè)備選方案對(duì)吧?
可隔離性就是這樣的一個(gè)備選方案,其意圖就是隔離你的代碼行為,哪怕它就是腐爛變質(zhì)成了不可維護(hù)的代碼,只要不影響其他的模塊,那么就還算是可控的。就像萬噸巨輪,底層的隔水艙總是一個(gè)個(gè)的獨(dú)立的,一個(gè)進(jìn)水了也不影響其他的,從而避免整體的失控。
6 途徑
還記得文章開始介紹的目標(biāo)和途徑的概念吧,上述的3個(gè)原則是我們的目標(biāo),那么想要達(dá)成這樣的目標(biāo)有哪些途徑可供使用呢?
6.1 命名
曾經(jīng)有這么一句話,計(jì)算機(jī)領(lǐng)域有兩大難題:命名和緩存失效。一個(gè)好名字的重要性不必多說了吧?此外我還有一個(gè)心得體會(huì):如果你覺得命名出現(xiàn)了困難,那么請(qǐng)從頭審視一下你的設(shè)計(jì),或許你走錯(cuò)了方向了。我認(rèn)為一旦出現(xiàn)了命名困難的問題,那絕對(duì)就是你的設(shè)計(jì)出現(xiàn)了問題。也許時(shí)你的方法職責(zé)太多了,你無法用簡(jiǎn)潔的名字描述清楚,也許是你的字段所表達(dá)的含義不清,導(dǎo)致你無法準(zhǔn)確的用一個(gè)簡(jiǎn)單的詞語描述它。
| 目標(biāo) | 效果 | 解釋 |
|---|---|---|
| 可理解性 | ++ | 增加可讀性。 |
| 可測(cè)試性 | 無 | 無影響。 |
| 可隔離性 | 無 | 無影響。 |
6.2 單一職責(zé)
幾乎每個(gè)人都明白單一職責(zé)的重要性,但是卻很容易就忽略它。比如下面的小例子:
// 1
public String sum(
final Collection<BigDecimal> bigDecimalCollection
) {
final BigDecimal sumResult = bigDecimalCollection
.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
final DecimalFormat format = new DecimalFormat("#,##0.00");
return format.format(sumResult);
}
// 2
public BigDecimal sum(
final Collection<BigDecimal> bigDecimalCollection
) {
return bigDecimalCollection
.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
1的職責(zé)是不是有點(diǎn)多?
| 目標(biāo) | 效果 | 解釋 |
|---|---|---|
| 可理解性 | ++ | 一個(gè)關(guān)注點(diǎn)使得代碼可理解性大大的提升。 |
| 可測(cè)試性 | ++ | 也使得測(cè)試更容易實(shí)施。 |
| 可隔離性 | ++ | 單一單一,那不就是隔離開了嗎? |
6.3 數(shù)據(jù)模型匹配業(yè)務(wù)
數(shù)據(jù)模型匹配的含義是說讓你的代碼真實(shí)的表達(dá)實(shí)際的業(yè)務(wù)意圖,而且這個(gè)意圖必須要落實(shí)到數(shù)據(jù)層面,而非代碼層面。簡(jiǎn)而言之就是讓你的數(shù)據(jù)體現(xiàn)你的業(yè)務(wù),而不是你的代碼體現(xiàn)你的業(yè)務(wù)。感覺有點(diǎn)繞噢,什么鬼意思?我舉個(gè)小例子:個(gè)稅計(jì)算
// 1
(empployee.salary - 3500) * taxRate;
// 2 employee.exemption = 3500
(empployee.salary - employee.exemption) * taxRate;
你覺得哪種更合適?1就是業(yè)務(wù)被體現(xiàn)在了代碼中,這時(shí)候2019年了,個(gè)稅免征額提高到了5000,你怎么辦?改代碼唄,3500改成5000不就完事了。對(duì),完事了,那么歷史的數(shù)據(jù)怎么辦?有人要對(duì)比一下新舊版本的差異,怎么算?沒辦法,你被逼著寫了兩個(gè)版本,2019年前一個(gè)版本的代碼,2019年后的一個(gè)版本,然后混亂就開始了。
所以根本問題在哪?就是因?yàn)?500這個(gè)數(shù)字看起來雖然不起眼,但是它本身是業(yè)務(wù)的一部分,結(jié)果卻被安置到了代碼中。這就是典型的數(shù)據(jù)模型不匹配業(yè)務(wù)。這種細(xì)節(jié)有時(shí)候一開始很難察覺到,但是一旦發(fā)現(xiàn)可能就已經(jīng)很難挽回了,代碼可以隨便改,但是已經(jīng)存在的歷史數(shù)據(jù)怎么辦? 上述的例子還好說點(diǎn),你可以刷一下歷史數(shù)據(jù)給補(bǔ)上去。但是很多時(shí)候數(shù)據(jù)一開始沒有記錄,后續(xù)就無論如何也無法修補(bǔ)了,導(dǎo)致你的代碼被死死的捆綁住,無法再添加新功能了。
筆者非常認(rèn)同Linus torvalds的一句話:“爛程序員關(guān)心的是代碼。好程序員關(guān)心的是數(shù)據(jù)結(jié)構(gòu)和它們之間的關(guān)系?!?sup>[1]。Git的數(shù)據(jù)結(jié)構(gòu)非常之穩(wěn)定,它的底層實(shí)際上是一個(gè)內(nèi)容尋址文件系統(tǒng),在這樣的一個(gè)底層數(shù)據(jù)結(jié)構(gòu)之上,十幾年來Git新增了n多個(gè)功能和命令,但是卻一致保持著的兼容性(你用Git早期版本初始化操作一個(gè)repo,到了現(xiàn)在的最新版依然是完全匹配的)。
| 目標(biāo) | 效果 | 解釋 |
|---|---|---|
| 可理解性 | ++ | 匹配的模型可以表達(dá)真實(shí)的業(yè)務(wù)意圖,沒有中間轉(zhuǎn)換的環(huán)節(jié),可以讓你再理解代碼時(shí)沒有額外的心智負(fù)擔(dān)。 |
| 可測(cè)試性 | + | 使得測(cè)試更能直觀的描述真實(shí)的業(yè)務(wù)行為。 |
| 可隔離性 | + | 合理的模型劃分可以有效的減少不必要的依賴,從而保持相對(duì)獨(dú)立。 |
6.4 抽象層級(jí)
把大象放進(jìn)冰箱需要幾步?
把冰箱門打開。
把大象放進(jìn)去。
把冰箱門關(guān)上。
就這么簡(jiǎn)單,這三件事都是在一個(gè)抽象的層級(jí)上的。那么再細(xì)化一些,打開冰箱門需要幾步?還有現(xiàn)在沒大象,我要去從動(dòng)物園先弄過來一個(gè),怎么辦?這些細(xì)節(jié)和上述的三個(gè)步驟是不是在一個(gè)抽象層級(jí)上? 肯定不是吧!但是我們通常很多時(shí)候都是在干著這樣的事情,比如業(yè)務(wù)代碼中夾雜著如何拼接SQL語句的代碼。當(dāng)你讀到這樣的代碼的時(shí)候會(huì)覺得很亂,為什么感覺亂?就是因?yàn)槠浜w了不同抽象層級(jí)的代碼在一起,導(dǎo)致你在前腳還在想著如何把大象放進(jìn)去這件事的時(shí)候,突然發(fā)現(xiàn)接下來的是我怎么才能從動(dòng)物園弄個(gè)大象出來這些瑣事。還記得上面的一個(gè)判斷非空的一小段代碼吧?
// 1
if(userList.isNotEmpty()){
}
// 4
if(userList.length() != 0){
}
4干的就樣的事情,雖然很細(xì)微,但是就是這樣一個(gè)一個(gè)細(xì)微的不同抽象層級(jí)的代碼混在一塊,就把你的代碼搞亂了,搞的可理解性急劇下降。
| 目標(biāo) | 效果 | 解釋 |
|---|---|---|
| 可理解性 | ++ | 閱讀代碼時(shí)避免分心去考慮一些不必要的細(xì)節(jié)問題。 |
| 可測(cè)試性 | ++ | 比如我用一個(gè)大象的毛絨玩具也可以完成第2步吧?這就大大的簡(jiǎn)化了測(cè)試的關(guān)注點(diǎn)和編寫。 |
| 可隔離性 | ++ | 屏蔽了一些底層的細(xì)節(jié)。 |
6.5 奧卡姆剃刀
這又是個(gè)什么鬼?怎么剃刀都出來了,還嫌發(fā)際線不夠高嗎?其實(shí)不是的,這個(gè)一個(gè)關(guān)于簡(jiǎn)單行的原則,也稱之為“如無必要,勿增實(shí)體”。就是說如果有兩個(gè)途徑可以完成同樣一件事情,那就選擇更簡(jiǎn)單假設(shè)更少的那一個(gè)。
| 目標(biāo) | 效果 | 解釋 |
|---|---|---|
| 可理解性 | + | 選擇更簡(jiǎn)單的有助于理解。 |
| 可測(cè)試性 | 無 | 無影響。 |
| 可隔離性 | 無 | 無影響。 |
7 一些誤區(qū)
看到這里估計(jì)有人要忍不住要批判我了:
可復(fù)用性呢?GoF23種設(shè)計(jì)模式都強(qiáng)調(diào)構(gòu)建可復(fù)用性的軟件,可復(fù)用性跑哪去了?被你吃了啊。
可靠性呢?健壯性呢?
高可用性呢?
等等吧,就像當(dāng)年軟工課程上羅列的各種指標(biāo),或者各種的模式和架構(gòu)等等。其實(shí)不是說這些東西不重要,或者我不認(rèn)可這些東西,我認(rèn)可,也理解它們的重要性。但是有一點(diǎn)要徹底搞清楚,哪些是我們的目標(biāo)?哪些是我們的途徑?
7.1 可復(fù)用性只是一種現(xiàn)象
可復(fù)用性難道是我們追求的目標(biāo)嗎?我的回答是:否,我們的目標(biāo)是軟件的可維護(hù)性!那么你說復(fù)用就會(huì)增加可維護(hù)性,其實(shí)不盡然,不合適的復(fù)用反而會(huì)降低可維護(hù)性,這是一把雙刃劍,借用著哥的一句話:“越通用越無用”。那么你說不是目標(biāo)也是途徑吧!那么我的回答是:也不是途徑,你這條途徑可能會(huì)違憲,你覺得它合適嗎?也不是目標(biāo),也不是途徑,那么它到底是什么?答:只是一種現(xiàn)象,如果你落實(shí)了上述的5條途徑中的某些途徑,你會(huì)發(fā)現(xiàn)你的代碼自然而然就可以復(fù)用了。
7.2 設(shè)計(jì)模式源自缺陷
首先我們看一下設(shè)計(jì)模式是什么: “是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。” 也就是說它是經(jīng)過驗(yàn)證的一些最佳實(shí)踐的經(jīng)驗(yàn)性代碼。那么問題來了,什么時(shí)候才需要最佳實(shí)踐?,當(dāng)你對(duì)你所使用的工具出現(xiàn)迷惑的時(shí)候,不太清楚怎么處理才好的時(shí)候,你需要借鑒一下其他人總結(jié)出來的比較好的處理方案才能完成你的工作的時(shí)候。這個(gè)處理方案,就是設(shè)計(jì)模式。那么此時(shí)你想一想,GoF23的設(shè)計(jì)模式是在彌補(bǔ)什么的缺陷?OO的啊,人家的副標(biāo)題是“可復(fù)用面向?qū)ο筌浖幕A(chǔ)”。
當(dāng)然設(shè)計(jì)模式也不是OO的專有的東西,凡是通用的那些已命名的最佳實(shí)踐,都可以稱之為設(shè)計(jì)模式。
7.3 OOP不是目的
很多時(shí)候在討論代碼的時(shí)候,看著代碼覺得不舒服,一言不合就互相給對(duì)方扣上了一頂帽子,你的代碼一點(diǎn)也不OO!這其實(shí)大可不必,OO是來解決一些問題的,但是它并不能解決全部問題,那么多static的類或者方法,它OO嗎?OO只是解決我們問題的一種途徑,也不是唯一的途徑,千萬不可把工具當(dāng)目的。
7.4 DDD帶來的問題比解決的問題更多
DDD自從誕生之初就面臨很多爭(zhēng)議。DDD本身出發(fā)點(diǎn)非常好(軟件核心復(fù)雜性應(yīng)對(duì)之道)。DDD是基于OO,在OO之上擴(kuò)充了很多概念,希望借此最大程度的發(fā)揮出OO的優(yōu)勢(shì)。但是其擴(kuò)充的概念太多了,而且千人千面,每個(gè)人心中的理解都不盡相同,而且可以說南轅北轍的都有,這就使得它非常難以在團(tuán)隊(duì)中達(dá)成理解上的共識(shí)。也就導(dǎo)致實(shí)施落地上的種種困難,即使一開始落地了一部分,隨著時(shí)間的推移,則會(huì)變得越來越難以為繼,好像側(cè)重點(diǎn)都跑到了我這么寫到底符合DDD的思想嗎?而對(duì)業(yè)務(wù)的關(guān)注的變成了二等公民,這簡(jiǎn)直是個(gè)災(zāi)難,這時(shí)候代碼的可理解性就非常脆弱了。所以根據(jù)奧卡姆剃刀原則,剃掉它是最優(yōu)的選擇。
8 總結(jié)
以上是筆者關(guān)于軟件設(shè)計(jì)的一些思考過程:筆者認(rèn)為其目標(biāo)是避免軟件的失控以及相關(guān)的途徑措施,以及對(duì)一些常見到的一些概念的看法。如有不妥之處,歡迎來討論。
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長(zhǎng)按上方微信二維碼 2 秒
感謝點(diǎn)贊支持下哈 
