軟件的度量對于設計者和開發者非常重要,之前只是對這些有一個簡單的了解。今天看來,了解的還遠遠不夠。
一個系統中的所有類的這三個度量能夠說明這個系統的設計上的一些問題(不是全部),這三個度量越大越不好。
如果一個類這三個度量很高,證明了這個類需要重構了。
以第一個度量來說,有下面的一個表格:
CC Value | Risk |
1-10 | Low risk program |
11-20 | Moderate risk |
21-50 | High risk |
>50 | Most complex and highly unstable method |
CC數值高,可以通過減少if else(switch case也算)判斷來達到目的;
可以通過減少類與其他類的調用來減少RFC;
通過分割大方法和大類來達到減少WMPC.
而Uncle Bob和Jdepend的度量標準應該算是另一個度量系統。
if(id!=null && name!=null) {
//do something
}else{
//do something
}
}
這種情況的CC不是2+1,而是2+1+1,依據是公式(1)。公式(2)應該是公式(1)的簡化版。Cyclomatic Complexity
(CC) = no of decision points + no of logical operations +1 (1)
Cyclomatic Complexity (CC) = number of decision points +1 (2)
Agile Software Development: Principles, Patterns, and Practices
參考了JDepend的參數和Uncle Bob的《》
(敏捷軟件開發:原則、模式與實踐)
java代碼:? |
public class Item implementsSerializable{ ? ? privateLong id = null; ? ? privateint version; ? ? privateString name; ? ? private User seller; ? ? privateString description; ? ? private MonetaryAmount initialPrice; ? ? private MonetaryAmount reservePrice; ? ? privateDate startDate; ? ? privateDate endDate; ? ? privateSet categorizedItems = newHashSet(); ? ? privateCollection bids = newArrayList(); ? ? private Bid successfulBid; ? ? private ItemState state; ? ? private User approvedBy; ? ? privateDate approvalDatetime; ? ? privateDate created = newDate(); ? ? //? getter/setter方法省略不寫,避免篇幅太長 } |
java代碼:? |
public interface ItemDao { ? ? public Item getItemById(Long id); ? ? publicCollection findAll(); ? ? publicvoid updateItem(Item item); } |
java代碼:? |
public class ItemDaoHibernateImpl implements ItemDao extends HibernateDaoSupport { ? ? public Item getItemById(Long id){ ? ? ? ? return(Item) getHibernateTemplate().load(Item.class, id); ? ? } ? ? publicCollection findAll(){ ? ? ? ? return(List) getHibernateTemplate().find("from Item"); ? ? } ? ? publicvoid updateItem(Item item){ ? ? ? ? getHibernateTemplate().update(item); ? ? } } |
java代碼:? |
public class ItemManager { ? ? private ItemDao itemDao; ? ? publicvoid setItemDao(ItemDao itemDao){ this.itemDao = itemDao;} ? ? public Bid loadItemById(Long id){ ? ? ? ? itemDao.loadItemById(id); ? ? } ? ? publicCollection listAllItems(){ ? ? ? ? return? itemDao.findAll(); ? ? } ? ? public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, ? ? ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)throws BusinessException { ? ? ? ? ? ? if(currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0){ ? ? ? ? ? ? throw new BusinessException("Bid too low."); ? ? } ? ? ? ? // Auction is active ? ? if( !state.equals(ItemState.ACTIVE)) ? ? ? ? ? ? throw new BusinessException("Auction is not active yet."); ? ? ? ? // Auction still valid ? ? if( item.getEndDate().before(newDate())) ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended."); ? ? ? ? // Create new Bid ? ? Bid newBid = new Bid(bidAmount, item, bidder); ? ? ? ? // Place bid for this Item ? ? item.getBids().add(newBid); ? ? itemDao.update(item);? ? ?//? 調用DAO完成持久化操作 ? ? return newBid; ? ? } } |
?
?
第二種模型,也就是Martin Fowler指的rich domain object是下面這樣子的:
一個帶有業務邏輯的實體類,即domain object是Item
一個DAO接口ItemDao
一個DAO實現ItemDaoHibernateImpl
一個業務邏輯對象ItemManager
java代碼:? |
public class Item implementsSerializable{ ? ? //? 所有的屬性和getter/setter方法同上,省略 ? ? public Bid placeBid(User bidder, MonetaryAmount bidAmount, ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid) ? ? ? ? ? ? throws BusinessException { ? ? ? ? ? ? ? ? // Check highest bid (can also be a different Strategy (pattern)) ? ? ? ? ? ? if(currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0){ ? ? ? ? ? ? ? ? ? ? throw new BusinessException("Bid too low."); ? ? ? ? ? ? } ? ? ? ? ? ? ? ? // Auction is active ? ? ? ? ? ? if( !state.equals(ItemState.ACTIVE)) ? ? ? ? ? ? ? ? ? ? throw new BusinessException("Auction is not active yet."); ? ? ? ? ? ? ? ? // Auction still valid ? ? ? ? ? ? if( this.getEndDate().before(newDate())) ? ? ? ? ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended."); ? ? ? ? ? ? ? ? // Create new Bid ? ? ? ? ? ? Bid newBid = new Bid(bidAmount, this, bidder); ? ? ? ? ? ? ? ? // Place bid for this Item ? ? ? ? ? ? this.getBids.add(newBid);? // 請注意這一句,透明的進行了持久化,但是不能在這里調用ItemDao,Item不能對ItemDao產生依賴! ? ? ? ? ? ? ? ? return newBid; ? ? } } |
java代碼:? |
public class ItemManager { ? ? private ItemDao itemDao; ? ? publicvoid setItemDao(ItemDao itemDao){ this.itemDao = itemDao;} ? ? public Bid loadItemById(Long id){ ? ? ? ? itemDao.loadItemById(id); ? ? } ? ? publicCollection listAllItems(){ ? ? ? ? return? itemDao.findAll(); ? ? } ? ? public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, ? ? ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)throws BusinessException { ? ? ? ? item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid); ? ? ? ? itemDao.update(item);? ? // 必須顯式的調用DAO,保持持久化 ? ? } } |
?
?
第三種模型印象中好像是firebody或者是Archie提出的(也有可能不是,記不清楚了),簡單的來說,這種模型就是把第二種模型的domain
object和business object合二為一了。所以ItemManager就不需要了,在這種模型下面,只有三個類,他們分別是:
Item:包含了實體類信息,也包含了所有的業務邏輯
ItemDao:持久化DAO接口類
ItemDaoHibernateImpl:DAO接口的實現類
由于ItemDao和ItemDaoHibernateImpl和上面完全相同,就省略了。
java代碼:? |
public class Item implementsSerializable{ ? ? //? 所有的屬性和getter/setter方法都省略 ? ?privatestatic ItemDao itemDao; ? ? publicvoid setItemDao(ItemDao itemDao){this.itemDao = itemDao;} ? ? ? ? publicstatic Item loadItemById(Long id){ ? ? ? ? return(Item) itemDao.loadItemById(id); ? ? } ? ? publicstaticCollection findAll(){ ? ? ? ? return(List) itemDao.findAll(); ? ? } ? ? public Bid placeBid(User bidder, MonetaryAmount bidAmount, ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid) ? ? throws BusinessException { ? ? ? ? ? ? // Check highest bid (can also be a different Strategy (pattern)) ? ? ? ? if(currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0){ ? ? ? ? ? ? ? ? throw new BusinessException("Bid too low."); ? ? ? ? } ? ? ? ? ? ? ? ? // Auction is active ? ? ? ? if( !state.equals(ItemState.ACTIVE)) ? ? ? ? ? ? ? ? throw new BusinessException("Auction is not active yet."); ? ? ? ? ? ? ? ? // Auction still valid ? ? ? ? if( this.getEndDate().before(newDate())) ? ? ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended."); ? ? ? ? ? ? ? ? // Create new Bid ? ? ? ? Bid newBid = new Bid(bidAmount, this, bidder); ? ? ? ? ? ? ? ? // Place bid for this Item ? ? ? ? this.addBid(newBid); ? ? ? ? itemDao.update(this);? ? ? //? 調用DAO進行顯式持久化 ? ? ? ? return newBid; ? ? } } |
?
?
在上面三種模型之外,還有很多這三種模型的變種,例如partech的模型就是把第二種模型中的DAO和
Manager三個類合并為一個類后形成的模型;例如frain....(id很長記不住)的模型就是把第三種模型的三個類完全合并為一個單類后形成的模型;例如Archie是把第三種模型的Item又分出來一些純數據類(可能是,不確定)形成的一個模型。
但是不管怎么變,基本模型歸納起來就是上面的三種模型,下面分別簡單評價一下:
第一種模型絕大多數人都反對,因此反對理由我也不多講了。但遺憾的是,我觀察到的實際情形是,很多使用Hibernate的公司最后都是這種模型,這里面有很大的原因是很多公司的技術水平沒有達到這種層次,所以導致了這種貧血模型的出現。從這一點來說,Martin
Fowler的批評聲音不是太響了,而是太弱了,還需要再繼續吶喊。
第二種模型就是Martin
Fowler一直主張的模型,實際上也是我一直在實際項目中采用這種模型。我沒有看過Martin的POEAA,之所以能夠自己摸索到這種模型,也是因為從02年我已經開始思考這個問題并且尋求解決方案了,但是當時沒有看到Hibernate,那時候做的一個小型項目我已經按照這種模型來做了,但是由于沒有O/R
Mapping的支持,寫到后來又不得不全部改成貧血的domain
object,項目做完以后再繼續找,隨后就發現了Hibernate。當然,現在很多人一開始就是用Hibernate做項目,沒有經歷過我經歷的那個階段。
不過我覺得這種模型仍然不夠完美,因為你還是需要一個業務邏輯層來封裝所有的domain
logic,這顯得非常羅嗦,并且業務邏輯對象的接口也不夠穩定。如果不考慮業務邏輯對象的重用性的話(業務邏輯對象的可重用性也不可能好),很多人干脆就去掉了xxxManager這一層,在Web層的Action代碼直接調用xxxDao,同時容器事務管理配置到Action這一層上來。
Hibernate的caveatemptor就是這樣架構的一個典型應用。
第三種模型是我很反對的一種模型,這種模型下面,Domain
Object和DAO形成了雙向依賴關系,無法脫離框架測試,并且業務邏輯層的服務也和持久層對象的狀態耦合到了一起,會造成程序的高度的復雜性,很差的靈活性和糟糕的可維護性。也許將來技術進步導致的O/R
Mapping管理下的domain object發展到足夠的動態持久透明化的話,這種模型才會成為一個理想的選擇。就像O/R
Mapping的流行使得第二種模型成為了可能(O/R Mapping流行以前,我們只能用第一種模型,第二種模型那時候是不現實的)。
?
?
既然大家都統一了觀點,那么就有了一個很好的討論問題的基礎了。Martin Fowler的Domain
Model,或者說我們的第二種模型難道是完美無缺的嗎?當然不是,接下來我就要分析一下它的不足,以及可能的解決辦法,而這些都來源于我個人的實踐探索。
在第二種模型中,我們可以清楚的把這4個類分為三層:
1、實體類層,即Item,帶有domain logic的domain
object
2、DAO層,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和實現類
3、業務邏輯層,即ItemManager,接受容器事務控制,向Web層提供統一的服務調用
在這三層中我們大家可以看到,domain
object和DAO都是非常穩定的層,其實原因也很簡單,因為domain object是映射數據庫字段的,數據庫字段不會頻繁變動,所以domain
object也相對穩定,而面向數據庫持久化編程的DAO層也不過就是CRUD而已,不會有更多的花樣,所以也很穩定。
問題就在于這個充當business workflow facade的業務邏輯對象,它的變動是相當頻繁的。業務邏輯對象通常都是無狀態的、受事務控制的、Singleton類,我們可以考察一下業務邏輯對象都有哪幾類業務邏輯方法:
第一類:DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。
ItemManager之所以要代理這種類,目的有兩個:向Web層提供統一的服務調用入口點和給持久化方法增加事務控制功能。這兩點都很容易理解,你不能既給Web層程序員提供xxxManager,也給他提供xxxDao,所以你需要用xxxManager封裝xxxDao,在這里,充當了一個簡單代理功能;而事務控制也是持久化方法必須的,事務可能需要跨越多個DAO方法調用,所以必須放在業務邏輯層,而不能放在DAO層。
但是必須看到,對于一個典型的web應用來說,絕大多數的業務邏輯都是簡單的CRUD邏輯,所以這種情況下,針對每個DAO方法,xxxManager都需要提供一個對應的封裝方法,這不但是非??菰锏?,也是令人感覺非常不好的。
第二類:domain
logic的方法代理。就是上面例子中placeBid方法。雖然Item已經有了placeBid方法,但是ItemManager仍然需要封裝一下Item的placeBid,然后再提供一個簡單封裝之后的代理方法。
這和第一種情況類似,其原因也一樣,也是為了給Web層提供一個統一的服務調用入口點和給隱式的持久化動作提供事務控制。
同樣,和第一種情況一樣,針對每個domain logic方法,xxxManager都需要提供一個對應的封裝方法,同樣是枯燥的,令人不爽的。
第三類:需要多個domain object和DAO參與協作的business
workflow。這種情況是業務邏輯對象真正應該完成的職責。
在這個簡單的例子中,沒有涉及到這種情況,不過大家都可以想像的出來這種應用場景,因此不必舉例說明了。
通過上面的分析可以看出,只有第三類業務邏輯方法才是業務邏輯對象真正應該承擔的職責,而前兩類業務邏輯方法都是“無奈之舉”,不得不為之的事情,不但枯燥,而且令人沮喪。
分析完了業務邏輯對象,我們再回頭看一下domain object,我們要仔細考察一下domain
logic的話,會發現domain logic也分為兩類:
第一類:需要持久層框架隱式的實現透明持久化的domain
logic,例如Item的placeBid方法中的這一句:
java代碼:? |
this.getBids().add(newBid); |
java代碼:? |
class Topic { ? ? boolean isAllowReply(){ ? ? ? ? Calendar dueDate = Calendar.getInstance(); ? ? ? ? dueDate.setTime(lastUpdatedTime); ? ? ? ? dueDate.add(Calendar.DATE, forum.timeToLive); ? ? ? ? ? ? Date now = newDate(); ? ? ? ? return now.after(dueDate.getTime()); ? ? } } |
from http://www.code365.com/web/122/Article/17927.Asp
Thomas Bayes,一位偉大的數學大師,他的理論照亮了今天的計算領域,和他的同事們不同:他認為上帝的存在可以通過方程式證明,他最重要的作品被別人發行,而他已經去世241年了。
18世紀牧師們關于概率的理論成為應用發展的數學基礎的一部分。
搜索巨人Google和Autonomy,一家出售信息恢復工具的公司,都使用了貝葉斯定理(Bayesian principles)為數據搜索提供近似的(但是技術上不確切)結果。研究人員還使用貝葉斯模型來判斷癥狀和疾病之間的相互關系,創建個人機器人,開發 能夠根據數據和經驗來決定行動的人工智能設備。
雖然聽起來很深奧,而這個原理的意思--大致說起來--卻很簡單:某件事情發生的概率大致可以由它過去發生的頻率近似地估計出來。研究人員把這個原理應用在每件事上,從基因研究到過濾電子郵件。
在明尼蘇達州大學的網站上能夠找到一份詳細的數學概要。而在Gametheory.net上的一個Bayes Rule Applet程序讓你能夠回答諸如“如果你測試某種疾病,有多大風險”之類的問題。
貝葉斯理論的一個出名的倡導者就是微軟。該公司把概率用于它的Notification Platform。該技術將會被內置到微軟未來的軟件中,而且讓計算機和蜂窩電話能夠自動地過濾信息,不需要用戶幫助,自動計劃會議并且和其他人聯系。
如果成功的話,該技術將會導致“context server”--一種電子管家的出現,它能夠解釋人的日常生活習慣并在不斷變換的環境中組織他們的生活。
“Bayes的研究被用于決定我應該怎樣最好地分配計算和帶寬,” Eric Horvitz表示,他是微軟研究部門Adaptive Systems & Interaction Group的高級研究員和分組管理者。“我個人相信在這個不確定的世界里,你不能夠知道每件事,而概率論是任何智能的基礎?!?
到今年年底,Intel也將發布它自己的基于貝葉斯理論的工具包。一個關于照相機的實驗警告醫生說病人可能很快遭受痛苦。在本周晚些時候在該公司的Developer Forum(開發者論壇)上將討論這種發展。
雖然它在今天很流行,Bayes的理論并不是一直被廣泛接受的:就在10年前,Bayes研究人員還在他們的專業上躊躇不前。但是其后,改進的數學模型,更快的計算機和實驗的有效結果增加了這種學派新的可信程度。
“問題之一是它被過度宣傳了,” Intel微處理器實驗室的應用軟件和技術管理經理Omid Moghadam表示。“事實上,能夠處理任何事情的能力并不存在。真正的執行在過去的10年里就發生了。”
Bayes啞元
Bayes的理論可以粗略地被簡述成一條原則:為了預見未來,必須要看看過去。Bayes的理論表示未來某件事情發生的概率可以通過計算它過去發生的頻率來估計。一個彈起的硬幣正面朝上的概率是多少?實驗數據表明這個值是50%。
“Bayes表示從本質上說,每件事都有不確定性,你有不同的概率類型,”斯坦佛的管理科學和工程系(Department of Management Science and Engineering at Stanford)的教授Ron Howard表示。
例如,假設不是硬幣,一名研究人員把塑料圖釘往上拋,想要看看它釘頭朝上落地的概率有多大,或者有多少可能性是側面著地,而釘子是指向什么方向的。形狀,成型過程中的誤差,重量分布和其他的因素都會影響該結果。
Bayes技術的吸引力在于它的簡單性。預測完全取決于收集到的數據--獲得的數據越多,結果就越好。另一個優點在于Bayes模型能夠自我糾正,也就是說數據變化了,結果也就跟著變化。
概率論的思想改變了人們和計算機互動的方式。“這種想法是計算機能夠更象一個幫助者而不僅僅是一個終端設備,” Peter Norvig表示。他是Google的安全質量總監。他說“你在尋找的是一些指導,而不是一個標準答案。”
從這種轉變中,研究獲益非淺。幾年前,所謂的Boolean搜索引擎的一般使用需要把搜索按照“if, and, or but”的語法進行提交,然后去尋找匹配的詞?,F在的搜索引擎采用了復雜的運算法則來搜索數據庫,并找出可能的匹配。
如同圖釘的那個例子顯示的那樣,復雜性和對于更多數據的需要可能很快增長。由于功能強大的計算機的出現,對于把好的猜測轉變成近似的輸出所必須的結果進行控制成為可能。
更重要的是,UCLA的Judea Pearl這樣的研究人員研究出如何讓Bayes模型能夠更好地追蹤不同的現象之間條件關系的方法,這樣能夠極大地減少計算量。
例如,對于人口進行大規模的關于肺癌成因的調查可能會發現它是一種不太廣泛的疾病,但是如果局限在吸煙者范圍內進行調查就可能會發現一些關聯性。對于肺癌患者進行檢查能夠幫助調查清楚習慣和這種疾病之間的關系。
“每一個單獨的屬性或者征兆都可能取決于很多不同的事情,但是直接決定它的卻是為數不多的事情,”斯坦佛計算機科學系(computer
science department at Stanford)的助理教授Daphne
Koller表示?!霸谶^去的15年左右的時間里,人們在工具方面進行了改革,這讓你能夠描繪出大量人群的情況?!?
和其他一些項目一樣,Koller是使用概率論技術來更好地把病癥和疾病聯系起來,并把遺傳基因和特定的細胞現象聯系起來。
記錄演講
一項相關的技術,名為Hidden Markov模型,讓概率能夠預測次序。例如,一個演講識別應用知道經常在“q”之后的字母是“u”。除了這些,該軟件還能夠計算“Qagga”(一種滅絕了的斑馬的名稱)一詞出現的概率。
概率技術已經內置在微軟的產品中了。Outlook Mobile Manage是一個能夠決定什么時候往移動設備上發出一封內勤的電子郵的軟件。它是從Priorities發展而來的,Priorities是微軟在 1998年公布的一個實驗系統。Windows XP的故障檢修引擎也依賴于概率計算。
隨著該公司的Notification Platform開始內置在產品中,在未來的一年中會有更多的應用軟件發布,微軟的Horvitz這樣表示。
Notification Platform的一個重要組成部分名為Coordinate,它從個人日歷,鍵盤,傳感器照相機以及其他來源收集數據,來了解某個人生活和習慣。收集的 數據可能包括到達的時間,工作時間和午餐的時間長度,哪種類型的電話或電子郵件被保存,而哪些信息被刪除,在某天的特定時間里鍵盤被使用的頻率,等等。
這些數據可以被用來管理信息流和使用者收到的其他信息。例如,如果一位經理在下午2:40發送了一封電子郵件給一名員工, Coordinate可以檢查該員工的日歷程序,然后發現他在下午2:00有一個會議。該程序還可以掃描關于該員工習慣的數據,然后發現該員工通常會在有 會議之后大約一個小時才重新使用鍵盤。該程序可能還能夠發現該名員工通常會在5分鐘之內回復該經理的電子郵件。根據上面這些數據,該軟件能夠估計出該員工 可能至少在20分鐘之內不可能回復該電子郵件,該軟件可能會把這條信息發送到該員工的手提電話上。同時,該軟件可能會決定不把別人的電子郵件也轉發出去。
“我們正在平衡以打攪你為代價所獲得信息的價值,” Horvitz表示。使用這個軟件,他堅持道,“能夠讓更多的人跟上事情的發展,而不被大量的信息所淹沒。”
Horvitz補充道,隱私和對于這些功能的用戶控制是確定的。呼叫者并不知道為什么一條信息可能會被優先或推遲處理。
微軟還把Bayes模型使用在其他的一些產品上,包括DeepListener 以及Quartet (語音激活),SmartOOF 以及TimeWave (聯系控制)。消費者多媒體軟件也獲益非淺,Horvitz表示。
Bayes技術不僅僅被應用在PC領域。在University of Rochester,研究人員發現一個人的步伐可以在一步前發生改變。雖然這種改變對于人類來說太過于細微,一臺和電腦連接在一起的照相機可以捕捉并跟蹤 這種動作。如果行走異常出現,計算機就能夠發出警報。
一個實驗用的安全照相機采用了同樣的原理:大部分到達機場的人都會在停車以后直接走向目的地,所以如果有人停了車,然后走向另一輛車就不太正常,因此就可能引發警報。今年秋天一個創建Bayes模型和技術信息的基本引擎將會公布在Intel的開發者網站上。
理論沖突
雖然該技術聽起來簡單易懂,關于它的計算可能卻比較慢。Horvitz回憶說他是斯坦佛20世紀80年代僅有的兩個概率和人工智能的畢業生之一。其他所有的人學習的是邏輯系統,采用的是“if and then”的模式和世界互動。
“概率論那時候不流行,” Horvitz表示。但是當邏輯系統不能夠預測所有的意外情況時,潮流發生了轉變。
很多研究人員開始承認人類的決策過程比原來想象的要神秘的多。“在人工智能領域存在著文化偏見,” Koller表示?!叭藗儸F在承認他們并不知道他們的腦子是如何工作的?!?
即便在他的時代,Bayes發現他自己置身于主流之外。他于1702年出生于倫敦,后來他成為了一名Presbyterian minister。雖然他看到了自己的兩篇論文被發表了,他的理論很有效,但是《Essay Toward Solving a Problem in the Doctrine of Chances》卻一直到他死后的第三年,也就是1764年才被發表。
他的王室成員身份一直是個謎,直到最近幾年,新發現的一些信件表明他私下和英格蘭其他一些思想家看法一致。
“就我所知,他從來沒有寫下貝葉斯定理,” Howard表示。
神學家Richard Price和法國的數學家Pierre Simon LaPlace成為了早期的支持者。該理論和后來George Boole,布爾數學之父,的理論背道而馳。George Boole的理論是基于代數邏輯的,并最終導致了二進制系統的誕生。也是皇室成員之一的Boole死于1864年。
雖然概率的重要性不容置疑,可是關于它的應用的爭論卻沒有停止過。批評者周期性地聲稱Bayes模型依賴于主觀的數據,而讓人類去判斷答案是否正確。而概率論模型沒有完全解決在人類思維過程中存在的細微差別的問題。
“兒童如何學習現在還不是很清楚,”IBM研究部門的科學和軟件副總裁 Alfred Spector這樣表示。他計劃把統計學方法和邏輯系統在他的Combination Hypothesis之中結合起來。“我最初相信是統計學的范疇,但是從某方面說,你將會發現不僅僅是統計學的問題?!?
但是,很有可能概率論是基礎。
“這是個基礎,” Horvitz表示。“它被忽略了一段時間,但是它是推理的基礎。”
最近在看< 敏捷軟件開發:原則、模式與實踐 >在網上找了一些資料,就把他們集合到了一起,便于自己學習開閉原則很簡單,一句話:“Closed for Modification; Open for Extension”——“對變更關閉;對擴展開放”。開閉原則其實沒什么好講的,我將其歸結為一個高層次的設計總則。就這一點來講,OCP的地位應該比SRP優先。
OCP的動機很簡單:軟件是變化的。不論是優質的設計還是低劣的設計都無法回避這一問題。OCP說明了軟件設計應該盡可能地使架構穩定而又容易滿足不同的需求。
為什么要OCP?答案也很簡單——重用。
“重用”,并不是什么軟件工程的專業詞匯,它是工程界所共用的詞匯。早在軟件出現前,工程師們就在實踐“重用”了。比如機械產品,通過零部 件的組裝得到最終的能夠使用的工具。由于機械部件的設計和制造過程是極其復雜的,所以互換性是一個重要的特性。一輛車可以用不同的發動機、不同的變速箱、 不同的輪胎……很多東西我們直接買來裝上就可以了。這也是一個OCP的例子。(可能是由于我是搞機械出身的吧,所以就舉些機械方面的例子^_^)。
如何在OO中引入OCP原則?把對實體的依賴改為對抽象的依賴就行了。下面的例子說明了這個過程:
05賽季的時候,一輛F1賽車有一臺V10引擎。但是到了06賽季,國際汽聯修改了規則,一輛F1賽車只能安裝一臺V8引擎。車隊很快投入了新賽車 的研發,不幸的是,從工程師那里得到消息,舊車身的設計不能夠裝進新研發的引擎。我們不得不為新的引擎重新打造車身,于是一輛新的賽車誕生了。但是,麻煩 的事接踵而來,國際汽聯頻頻修改規則,搞得設計師在“賽車”上改了又改,最終變得不成樣子,只能把它廢棄。
為了能夠重用這輛昂貴的賽車,工程師們提出了解決方案:首先,在車身的設計上預留出安裝引擎的位置和管線。然后,根據這些設計好的規范設計引擎(或是引擎的適配器)。于是,新的賽車設計方案就這樣誕生了。
?
Liskov替換原則—— LSP?
子類型必須能夠替換它的基類型
OCP作為OO的高層原則,主張使用“抽象(Abstraction)”和“多態(Polymorphism)”將設計中的靜態結構改為動態結構,維持設計的封閉性。
“抽象”是語言提供的功能。“多態”由繼承語義實現。
如此,問題產生了:“我們如何去度量繼承關系的質量?”
Liskov于1987年提出了一個關于繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保超類所擁有的性質在子類中仍然成立?!币簿褪钦f,當一個子類的實例應該能夠替換任何其超類的實例時,它們之間才具有 is-A關系。
該原則稱為Liskov Substitution Principle——里氏替換原則。林先生在上課時風趣地稱之為“老鼠的兒子會打洞”。^_^
我們來研究一下LSP的實質。學習OO的時候,我們知道,一個對象是一組狀態和一系列行為的組合體。狀態是對象的內在特性,行為是對象的外在特性。LSP所表述的就是在同一個繼承體系中的對象應該有共同的行為特征。
這一點上,表明了OO的繼承與日常生活中的繼承的本質區別。舉一個例子:生物學的分類體系中把企鵝歸屬為鳥類。我們模仿這個體系,設計出這樣的類和關系。
?
類“鳥”中有個方法fly,企鵝自然也繼承了這個方法,可是企鵝不能飛阿,于是,我們在企鵝的類中覆蓋了fly方法,告訴方法的調用者:企 鵝是不會飛的。這完全符合常理。但是,這違反了LSP,企鵝是鳥的子類,可是企鵝卻不能飛!需要注意的是,此處的“鳥”已經不再是生物學中的鳥了,它是軟 件中的一個類、一個抽象。
有人會說,企鵝不能飛很正常啊,而且這樣編寫代碼也能正常編譯,只要在使用這個類的客戶代碼中加一句判斷就行了。但是,這就是問題所 在!首先,客戶代碼和“企鵝”的代碼很有可能不是同時設計的,在當今軟件外包一層又一層的開發模式下,你甚至根本不知道兩個模塊的原產地是哪里,也就談不 上去修改客戶代碼了??蛻舫绦蚝芸赡苁沁z留系統的一部分,很可能已經不再維護,如果因為設計出這么一個“企鵝”而導致必須修改客戶代碼,誰應該承擔這部分 責任呢?(大概是上帝吧,誰叫他讓“企鵝”不能飛的。^_^)“修改客戶代碼”直接違反了OCP,這就是OCP的重要性。違反LSP將使既有的設計不能封 閉!
修正后的設計如下:
?
但是,這就是LSP的全部了么?書中給了一個經典的例子,這又是一個不符合常理的例子:正方形不是一個長方形。這個悖論的詳細內容能在網上找到,我就不多廢話了。
LSP并沒有提供解決這個問題的方案,而只是提出了這么一個問題。
于是,工程師們開始關注如何確保對象的行為。1988年,B. Meyer提出了Design by Contract(契約式設計)理論。DbC從形式化方法中借鑒了一套確保對象行為和自身狀態的方法,其基本概念很簡單:
以上是單個對象的約束條件。為了滿足LSP,當存在繼承關系時,子類中方法的前置條件必須與超類中被覆蓋的方法的前置條件相同或者更寬松;而子類中方法的后置條件必須與超類中被覆蓋的方法的后置條件相同或者更為嚴格。
一些OO語言中的特性能夠說明這一問題:
可以看出,以上這些特性都非常好地遵循了LSP。但是DbC呢?很遺憾,主流的面向對象語言(不論是動態語言還是靜態語言)還沒有加入對DbC的支持。但是隨著AOP概念的產生,相信不久DbC也將成為OO語言的一個重要特性之一。
依賴倒置原則—— DIP?
a:高層模塊不應依賴于底層模塊,兩者都應該依賴于抽象
b:抽象不應該依賴于細節,細節應該依賴于抽象
接口隔離原則—— ISP?
使用多個專門的接口比使用單一的總接口總要好。換而言之,從一個客戶類的角度來講:一個類對另外一個類的依賴性應當是建立在最小接口上的。
原則過于臃腫的接口是對接口的污染。不應該強迫客戶依賴于它們不用的方法。
My object-oriented umbrella(摘自Design Patterns Explained)
Let me tell you about my great umbrella. It is large enough to get into! In fact, three or four other people can get in it with me. While we are in it, staying out of the rain, I can move it from one place to another. It has a stereo system to keep me entertained while I stay dry. Amazingly enough, it can also condition the air to make it warmer or colder. It is one cool umbrella.
My umbrella is convenient. It sits there waiting for me. It has wheels on it so that I do not have to carry it around. I don't even have to push it because it can propel itself. Sometimes, I will open the top of my umbrella to let in the sun. (Why I am using my umbrella when it is sunny outside is beyond me!)
In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors. Most people call them cars.
實現方法:
1、?使用委托分離接口
2、?使用多重繼承分離接口
想到一個朋友說的話:所有的接口都只有一個方法,具體的類根據自己需要什么方法去實現接口