標準來源於目標
前文說過,評價架構好壞是一個很主觀的東西。既然大家寫出來的程式都能跑,憑什麼就說你架構好,我的架構就差?拿出來大家評評理,張三說好,李四說不行,王五說將就……究竟誰說了算?現在已經不是一個迷信權威的時代了,所以不管你多少光環加持,你都得說出子醜寅卯來,都得服眾才行。
我覺得,這種現象的產生,拋開“同行相輕”和“流派之爭”之類無厘頭的東西,一個很重要的原因就是沒有明確判斷標準。所以在網上,常常就出現這樣一種很熱鬧很奇葩很無奈的現象:我和你說效能,你跟我說安全;我跟你說安全,你跟我說擴充套件;我跟你說擴充套件,你跟我說維護;我跟你說維護,你跟我說成本……
這是一個很簡單的道理,沒有標準,就無法進行評判!所以,如果不能統一一個評判架構優劣的標準,我們永遠無法達成一致。你說有標準啊,效能安全可擴充套件……但這樣還是不行,標準過多,一樣等同於沒有標準。假設以100分為滿分,效能佔多少分?安全佔多少分?可擴充套件又佔多少分?因為要想提高效能,就可能就要犧牲可擴充套件性;要想安全,就會犧牲效能;要想……就會……;哪一方面更重要,哪一些可以犧牲?如果不是胸有成竹的話,最後還是會左支右絀手忙腳亂,亂成一鍋粥。
所以我再提出一個觀點:以是否實現架構師的設計目標為標準。如果說一個系統的架構,最終實現了架構師的設計目標,我們就可以說這是一個好架構;如果說沒有能實現架構師的目標,這就是一個不那麼好的架構。
“等一下等一下”,你要是反應夠敏銳的話,肯定會跳起來,“這是不是太主觀(兒戲)了?我隨便一堆爛程式碼,然後告訴你,‘是啊,我設計的目的就是讓他爛,越難越好’,按你的邏輯,這樣也行?”(⊙_⊙),嗯,你要是這樣玩兒我還真沒辦法。但是話又說回來,要讓一堆程式碼能跑又還夠“爛”,也還不是一件容易的事,你覺得呢?
總之,我希望大家能明白我的意思:架構師開始一個新專案,應當設立一個適當的設計目標;然後通過架構,努力實現其預定目標。如果最終系統的執行,符合其設計預期,我們就可以說:這個架構不錯還行!反正,架構就出了問題。
我們的目標
軟體行業有各種各樣的系統,每一種系統的開發都可能會有不同的目標。比如導彈發射的系統,我們可以想象,目標(甚至是基本要求)肯定是:1、穩定(絕對不能走火);2、迅速反應(不允許按下發射按鈕後一分鐘導彈才開始發射)。你可能覺得這種要求很好啊!任何系統不都是應該滿足這樣要求的嗎?比如我在淘寶買T恤,結果給我發一條丁字褲,這怎麼行?一個網頁半天打不開還有理了?我還真得答一句,它就是有理了。“存在即合理”,這裡的合理,合理在成本。我們目前日常使用到的絕大部分軟體,都是有bug的,而且是一堆的bug,但我們仍然在使用它們。如果你想使用像“導彈發射”一樣穩定精確迅捷的軟體,可能最後的結果只有一個:你用不起。(請自行腦補)
所以,其實我們是做了一個妥協,“便宜點,將就用吧”。我們為了達到我們的基本目的,犧牲掉一些“無關緊要”的東西。對於很多追求卓越的程式設計師來說,這種犧牲妥協是難以接受的。“白玉微瑕,你讓我怎麼能夠接受?”——但很多時候,你必須接受。這個問題這個觀點,我們會在整個系列中不斷的提及。請試著接受;如果你暫時還不能接受,請牢記:沒有犧牲,就沒有勝利!
那麼,我們的策略是:特色突出、整體均衡。說得更直白一點:有亮點,沒硬傷。這就夠了!而我們的亮點就是:可維護性。(注意:不是可擴充套件,可維護性包含可擴充套件,但不僅僅是可擴充套件)
為什麼是可維護性
幸或者不幸,我進入軟體行業之後,絕大部分的工作是幾乎所有程式設計師都不齒厭惡的維護。我曾經維護過一個有十年曆史的、糅合了C、VB、java、C#各種語言在內的一個物流系統的部件。我在那家公司工作了一年多,說實話,直到我離職,對整個系統,我連邊都沒摸到——這個系統太大了,而且連我們公司都只是其主營公司眾多外包公司中的一個。
在我花了兩週的時間找到一個bug的位置之後,我以為我終於明白了為什麼會說:“維護和開發的花費比是80:20”。但這只是我以為——現實更加殘忍:差不多一個月後,我又花了一個星期的時間,找到了另外一個bug的根源,正是我fix前一個bug所產生的。我淚流滿面,有沒有?腦子裡一下就蹦出個詞:“按下了葫蘆浮起了瓢”!總之,如果fix前一個bug就會導致後一個bug;如果fix後一個bug,就會導致前面的bug。我忘了最後是怎麼處理這個問題的,依稀記得是讓專案經理去和稀泥去了。因為這不是一個很關鍵很常用的功能,所以最後大概是不了了之吧。
後來我瞭解到,很多的開發專案,是這樣一個流程:一群人根據文件開始開發,幾個月後通過驗收上線;然後開發團隊解散,留下一兩個專案組裡最菜的菜鳥做“維護”。Game Over!皆大歡喜。這種現象,在各種外包團隊(尤其是以專案計價的廉價外包團隊)中更加的突出(這或許也是大家普遍歧視外包公司的一個原因?)
既然是這樣一種開發模式,很多開發人員根本體會不到維護的痛苦。在他們看來,“維護嘛,修修補補,加一兩個if...else而已,讓我們開發人員做更高大上的工作吧!”但他們也不是總這麼幸運,有時候,他們會被抓去“填坑”。據說最通常的做法,就是在“老坑”周圍再挖一堆“新坑”,填平之前的老坑即可。周而復始,直到有一天,“受不了啦!我們重寫吧!”——等等,為什麼不重構?呵呵,好問題,你覺得呢?
需求變更
很多程式設計師把這種困境歸咎於“需求變更”。如果不是那些傻逼客戶一天到晚的改需求,我一定會做出一個完美的作品!
或許是因為我是半路出家的原因,和很多程式設計師相反,我覺得:不是需求變更驅動著軟體的不斷更改,而是“軟體可以隨意更改”的這種特性刺激了不斷的需求變更。你裝修好的房子,是不是住一段時間之後就會覺得這裡那裡不合適?這裡少了一個插座,陽臺上該加一個龍頭,櫥櫃用著不順手……“要是能改改就更好了!”,只是這樣的改動太費力,所以大多數時間我們都還是算了。但軟體可以!理論上怎麼改都可以。想想軟體真的是一種很特殊的商品——它是可以交付“半成品”的。你先用著,如果有問題我再改改,有新需求我再改改,一直可以改到面目全非。沒有在其他傳統行業裡待過的程式設計師無法理解,“可以隨意更改”是一種多麼出色的特質。這意味著產品可以自我進化,應對各種變化,可以永生!想象這樣一臺“汽車”,開始可以在馬路上跑,過段時間改一下就可以在水裡遊,再拆裝一下可以當摩托拉風,堵車的時候展開翅膀……這是什麼樣一種屌爆天的體驗啊?
所以,“擁抱變化”絕不是一句口號,這是一種胸懷。
作為示例的這兩個系統,我是希望能用他們一輩子的。但我甚至無法想象一年之後他們會是什麼樣子——他們需要接受市場的檢驗,應對技術的升級換代,會有各種想象不到的變化。所以,可維護性無疑是必須放到首位的。
為了可維護
明確了架構的首要目標,我們就可以做一些基礎的選擇了。比如開發語言,可是是物件導向的C#,不需要“效能卓越”的C。
說道“物件導向”,可能有些同學就會比較high,腦子裡就會冒出“抽象”、“封裝”、“設計模式”等各種高大上的東西出來。但我不得不提醒你們:首先,這些都是微觀層面考慮的東西,而架構是巨集觀的;然後,這些都不是架構,而是潤滑黏合支援架構的東西;最後,在其他條件不變的情況下,系統中這些東西用得越少,說明架構越好。
我們以“設計模式”為例。大家在學習設計模式的過程中有沒有這樣一種困惑,“這樣繼承封裝多型亂七八糟的繞來繞去的幹嘛?”我花了很長一段時間才明白,要理解設計模式,必須要明白三個字:“不得已”。是迫不得已,才用設計模式來解決一些特定的問題,而不是說正常的程式碼就應該這樣寫!這種迫不得已,有很多種原因。個人覺得最容易理解的就是“介面卡模式”,因為出現了介面的衝突,所以我們不得不進行適配。但一個很自然的問題就是:為什麼不直接改介面讓他們自然融洽呢?這不是一種更自然更直觀的解決方案嗎?答案很有可能就是因為架構——大的架構已經確立,區域性必須服從整體。那麼,如果一個完全理想化的架構,是不是根本就不應該出現這種問題介面衝突的問題,因而根本就不需要這種設計模式?
所以,我說設計模式之類的東西是潤滑劑是黏合劑,他們的作用是彌補架構的區域性缺陷,更好的支撐架構。更極端的一種說法可以送給痴迷於設計模式的同學:設計模式是藥,沒病就不要吃藥!
那麼,為了可維護性,架構中究竟應該注意些什麼?這是一個很大的話題,開篇我們只說一點。
模組劃分
模組有大有小,大可以是一個分層一個專案,小可以是一個方法一個類。我們通常的做法是由大到小,逐步細分。
模組的劃分是相當的考驗架構能力的。良好的模組劃分,能夠讓我們方便的安排人手、合理的組織專案進度、迅速的定位程式碼……各種好處說都說不完。所以還是說說不好的模組劃分有什麼問題更容易一些,嗯,這個好像根本就不要說,想想你在一堆亂七八糟的程式碼裡不斷的F11的情形吧!
我個人認為,模組劃分的難度在於“整齊”和“靈活”之間取捨。通常來說,大的模組我們都是“一刀切”,著重強調的是“整齊”,比如口熟能詳的UI層、BLL層和DAL層,但這種“一刀切”的做法,更多的是一種無奈。我們的人類的思維侷限決定了我們在考慮複雜問題時無法深入到每一個細節,所以只能先“大而化之”的把一個複雜問題先進行簡單化。這樣帶來的一個嚴重的副作用就是,限制了程式碼的靈活性;而靈活性,正是應對複雜變化的有效武器。所以,在更小一些的模組(比如說:類)裡,我們引入了豐富多彩的抽象繼承設計模式等一系列充滿各種靈活性的機制,以彌合“一刀切”造成的問題。這一鬆一緊一張一弛中“度”的掌握,就只能說是一種藝術了。
模組劃分,籠統的說教用處不大,我們將在後面的文章中結合具體情況逐一說明。但我希望大家能夠明白:模組劃分是必須的——這種必須,是一種無可奈何的選擇。所以,喜歡從頁面直接寫sql到資料庫的同學,老大讓你把你的程式碼拆成幾段放到不同地方的時候,不要嫌麻煩;喜歡把一個簡單專案切成七層的同學,先仔細想不想這樣做是不是真的有必要。
程式碼之外
為了程式碼能夠長期有效的維護,我們還需要做很多工作,比如良好的文件、完善的專案管理流程。但我想說的,還是不是這個,而是程式碼之外的因素對專案架構的影響。比如開發團隊的背景能力偏好,一群C#程式設計師,你一定要整個node.js,這純粹是給自己找不痛快。除了這些稍稍用腦袋想一想就能明白的東西,有一件事,很多程式設計師並沒有意識到。
架構的一個天然目的就是:讓程式碼更智慧讓程式設計師更傻瓜。換一張說法就是,架構要“創造便利,讓程式設計師更關注業務”。
這可能是一個讓程式設計師感到悲哀的事實。正如機械師不停的發明,讓機器變得越來越聰明,取代流水線上的工人,最終取代了他們自己。從某種意義上說,我們都是自掘墳墓的人。一個良好的架構,就應該是讓每一個普通開發人員,都是一個個儘量廉價隨時可以替換的螺絲釘,這樣才能保證系統永遠健康正常的執行下去。告訴你這個事實可能讓你一整天都不開心,但接受這個事實之後能幫助你在工作中變得更加的“心平氣和”。螺絲釘就要有螺絲釘的覺悟;更何況,當好一顆螺絲釘也不是一件很容易的事。
(未完待續)
說說我專案的進展吧!
請暫時忽略創業家園,新版釋出後會予以通知。
我發現有些同學已經加入了任務管理系統,但我還沒給你們任何許可權。因為你們系統之前是我們專案內部使用,所以很多許可權方面的功能做得還不夠完善。請加入QQ群,或關注我的部落格,授權後會予以通知。
另外,很抱歉暫時實在還不能挨個1對1的回答你們的問題,因為這些問題差不多都是重複的,所以我整理了一個簡單的使用說明。請首先試著閱讀該說明,然後再提出問題。
最後,歡迎大家試用任務管理系統,提出寶貴意見。
最後最後吐槽:異常異常苦逼的文件編寫啊!磨磨蹭蹭的搞了兩週,還是不太滿意。但無論如何,醜媳婦總得見公婆,先就這樣吧。唉!我哪裡缺程式設計師,最缺的是一個能整理文件的軟妹紙啊……