總結
軟體開發的藝術
理想主義,經驗主義和無緒
文藝復興時期,現代科學產生了兩個重量級理論: 理性主義和經驗主義。
理性主義認為理智是資訊的首要來源。給出一個假設,只要通過思考就能理解和描述這個世界,如著名的伽利略自由落體實驗。
經驗主義則認為人類對世界認識的主要來源是經驗。
我們開一輛車,不必知道其內部實現細節。
如果孤立地基於兩種極端的方式來觀察世界都是片面的。對大多數人來說,懵懂無知是一種生活方式,也是理性主義和經驗主義結合在一起的結果。今天的程式開發和軟體工程方法也是如此。
軟體的演變過程
- 上世紀40年度,用機器語言, 那時debug的時候可能還得帶一個扳手
- 然後 FORTRAN ,他允許程式設計師只關心數學公式而不是內部機器內部實現 ---- 經驗主義
- 然後COBOL來了, 他簡化了資料庫的操作 ---- 經驗主義
- 當然同時期lisp出現了,他更強調純粹的數學模型---理性主義
- 然後cpp, 然後java
我們可以發現在軟體演變的過程中, 理性主義幾乎已無存身之處。
因為軟體的趨勢是: 程式設計師在可以不深入瞭解很多內容的情況下就可以寫出非常好的程式碼。
大型軟體是如何開發的
現狀: 開發團隊往往直接複用現有的一些軟體框架,完全不重視這些重量級的框架是否超過我們的需要的。現代軟體都是基於大型元件的方式進行組裝的。 我要一個web伺服器就裝一個tomocat, 要一個資料庫就裝個mysql。 這完全是一種推土機式的開發方式,不管你的元件有多大, 總會找到合適的推土機把他推上去。 執行效率太差 就 加記憶體,搞伺服器叢集,
這種方法是好還是壞呢?實際上絕大多數公司已經用這種方式了。
因為推土機式的工作方式可以使你在不關注內部細節的情況下,也可以得到不錯的結果。 我們可以在不瞭解汽車原理的情況下,可以把汽車開得很好。 在寫win32程式時候,也不必瞭解系統是怎麼實現。 我們只需要關注windows 系統API, 以及這些API的功能。
理解一個系統有兩個層面:
- 淺層理解: 緊限於瞭解使用方法
- 深層理解: 理解其原理
而在軟體開發中, 一般只要做到淺層理解就可以了。
我們說明軟體開發其實是一個經驗的積累過程,並且可以是複用前人的經驗積累的。
設計API的動力之源
好的API可以使功能的使用者聚焦在使用層面而不是其內部細節
為什麼需要好的API:
- 分散式開發: 通常我們一個程式部署由一個人能獨立完成的
- 模組化應用程式 : 我們的程式是分模組的,不同模組的互動就是用API
- 交流互通才是一切: 模組之間相互依賴
- 開發第一個版本通常比較容易
設計API過程中遇到的最大問題--- 不斷變化的需求
一個軟體開發的生命週期:
- 第一版吧總是非常漂亮的
- 快樂總是短暫的
- 軟體熵不斷增加
- 天哪 千萬不要動他
- 重新開發一個版本吧。
變化是萬惡之源。
那麼如何才能設計好的API
評價API好壞的標準
第一步先確認什麼才叫好。
很多人認為所謂的API,不過是類和方法。但是這是比較片面的。
強調一點, 我們為什麼需要開發好的API:我們希望能夠將大塊的構建模組,”無緒“地集合成應用程式。
那麼如何評價一個API的質量: 漂亮? 但是評價漂亮的標準是很主觀的。我們應該設計易於使用、廣為接受且富有成效的API。我們可以有一下幾個方面來衡量一個API的好壞。
- 可理解性: 每個人的世界觀都會限制自己d視野,所以對於一個優秀的API來說,他涉及的概念都要在使用者的可理解範圍之內, 即使有新的概念也應該是漸進式的。
- 一致性: 向下維持相容
- 可見性: 最好提供一個入口用來作為使用者API的起點。 為什麼大家都喜歡開源,因為開源很多東西網上可以直接拷貝。
- 簡單的任務應該有簡單的方案: 所以API應該是分層的。
- 保護投資: 善待API的使用者。 儘量想辦法讓API漂亮點。如方法名,如結構等。 在釋出第一版之前這些都是非常合適的。但是發完第一版以後我們要保證我嗎的程式碼改動不會影響正在執行的程式碼了。
實現API的步驟
第二步弄清楚寫API的步驟
寫一個API有三個步驟:用例, 場景, 文件。
一個用例就是一種用法的描述,他指出使用者可能要面臨的問題,而這個問題不是一個具體的問題,而是很多問題的抽象。
舉個例子
用例: 設計一個資料庫管理器,他的功能是註冊JDBC驅動。
場景:對用例的回答。我們把API要描述的每一個功能下列出來:
- 註冊有一個JDBC可以寫一個能夠描述驅動的XML, 有格式
- 這個XML放置在DataBase/JDBCDrivers目錄下
- 用URL來表示驅動地址
注意一些設計原則
第三步 學習一些設計原則和通用方法
- 只公開你要公開的內容
- 面向介面而非實現進行程式設計
- 模組化架構
- 宣告式程式設計
只公開你要公開的內容
我們所設計的API都會被可能誤用。幾乎所有的API設計者都會有這樣的共識:一個人API設計的時間越長,他設計的API公開的內容會越少。
設計API的幾種方法:
1. 方法優於欄位
這個不用說了。 geter setter。 這樣做會有很多改變的餘地。 如計算、轉換、校驗、覆蓋
2.工廠方法優於建構函式
工廠方法會帶來很大的靈活性,三個好處:
- 返回不一定是宣告的型別可以是子類
- 構造的物件可以被快取
- 同步的控制。 可以都構造物件前後的程式碼進行控制。
3. 讓所有的內容都不可改變
通常情況下, 在設計一個類的時候,如果不考慮讓擁有子類,那就不應該讓這個類被繼承。 用final 來修飾。 還有些其他方案來: 不公開建構函式轉而提供工廠方法。把大部分方法變為final或者private
4. 避免濫用setter方法
一個寶貴的教訓: 如無必要,絕對不要再正式的API中宣告setter方法。
5. 儘可能通過友元的方式來公開功能
在java中,所謂的友元就是用預設的package方式訪問,即允許同一個包內的程式碼進行訪問。
有個實際的例子
6. 賦予物件建立者更多的權利
7. 避免暴露深層次繼承
避免深層次的繼承,定義程式的介面,並讓使用者來實現這些介面。 如果一個類繼承了某個類或者介面,那麼就可以作為響應的類或介面被使用。
如在swing中, frame間接繼承了compoment,這樣就表示所有使用compoment的地方,都可以使用frame. 實際上frame繼承 compoment是為了複用compoment的程式碼。這是一種典型的物件導向的複用的誤用。 所以如果發現繼承提醒超過2層,一定要想清楚“我是在設計API還是在複用程式碼”,如果是後者,則做好子類化準備。
面向介面而非實現進行程式設計
本質上講,這個原則倡導的是,當我們寫一個函式或一個方法時,我們應該引用相應的介面,而不是具體的實現類。介面提供了非常優秀的抽象歸納,讓我們的開發工作變得容易很多。 讓API的使用者和API的實現者解耦出來。
模組化架構
隨著軟體規模的增大以及功能的複雜性增加。只要程式碼開始訪問其他無關模組的內容,那麼架構的退化不可避免。模組化能有效變緩這種退化。
模組化的目的非常簡單,就是要實現程式中各個組成部分的鬆耦合。如果兩個模組是獨立的,那兩個模組就不需要知道對方的存在。如果兩個模組要互動,那麼他們應該通過具有良好定義的介面來進行互動。
宣告式程式設計
宣告式程式設計的基本思路, 不是讓API使用者一步一步告訴程式如何做,而只是需要告訴程式他們要的結果,然後交給API去完成。宣告式程式設計的好處是能在較高的抽象層次來定義操作。
比如寫一個資源管理API: API的使用者很容易找到一個方法去註冊一個功能,執行一下成功了,然後不再往下繼續找登出的方法了。 宣告式程式設計就是解決這個問題的一劑良藥: 開發人員只需要宣告註冊什麼,響應的登出和清理由系統完成。
極端的意見有害無益
最後提醒一點,任何極端的想法都是有害的,在軟體框架設計中也一樣, 有以下單不僅限幾個點:
- API必須是漂亮的
- API必須是正確的: 有時候易用性和正確性還重要。比如巨大檔案
- API應該儘量簡單
- API必須是高效能
- API必須絕對相容
- API必須是對稱的
團隊協作
現代的大型工程很少是有一個人單獨開發完成的,所以團隊協作非常重要。一下幾個點能很好幫助在大型軟體工作中完成好的API設:
- 在提交程式碼時候進行程式碼稽核
- 為API提供文件
- 盡職盡責的監控者
- 接受API的補丁
以上非常簡單,但是在外面平常工作中缺少的就是執行。