總結
軟件開發的藝術
理想主義,經驗主義和無緒
文藝復興時期,現代科學產生了兩個重量級理論: 理性主義和經驗主義。
理性主義認為理智是信息的首要來源。給出一個假設,只要通過思考就能理解和描述這個世界,如著名的伽利略自由落體實驗。
經驗主義則認為人類對世界認識的主要來源是經驗。
我們開一輛車,不必知道其內部實現細節。
如果孤立地基於兩種極端的方式來觀察世界都是片面的。對大多數人來說,懵懂無知是一種生活方式,也是理性主義和經驗主義結合在一起的結果。今天的程序開發和軟件工程方法也是如此。
軟件的演變過程
- 上世紀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的補丁
以上非常簡單,但是在外面平常工作中缺少的就是執行。