上一節
下一節
OO In Lucene Search 物件導向的Lucene
Nutch正式立項以後, Apache 基金會的incubator裡現存Lucene相關項目還有一個: Lucene4C. 顧名思義, 這是一個完完全全用C做成的搜尋引擎, 很多對用Java做SE有意見的哥們熱烈盼望這個東東快點孵化出來. 不過我擔心已經已經用慣Lucene Java的同志們對Lucene4C的熱情純屬葉公好龍, 理由很簡單, 就像Lucene4C的說明中提到的, Lucene4C和Lucene Java的相容是"底層相容"即演算法和索引格式的相容, Lucene4C只是為那些慣於C代碼的程式員而專門設計. 換言之, 雖然我們可以用Lucene4C搜尋LuceneJava的索引或反過來用, 但絕對別指望Doug Cutting們會提供一個類似Java的介面API. 即使不提編程風格的差異, 那些只會把繼承後的對象扔給API的傢伙們怎麼能在沒有類的世界中生存呢, 沒有了對象封裝, 沒有OCP/SRP, 還要處理種種指標, 光是調用memset/strcpy就夠他們抓狂了.
從top level來講, 將OOD的作品移植到C, 不僅是個浩大的工程, 還是個沒法預期結果的定時炸彈. 不過我還是很盼望看到Lucene4C的成品, 我想抓住它的代碼一行一行的讀. 兩種截然不同的設計理念生出一對雙胞胎, 我要瞧瞧它們有什麼不同, 相信Apache的天才們能讓我在對比中學到很多.
所以要先搞清楚OO在Lucene中如何體現.
在利用TermQuery這個最基本的Query研討查詢機制後, 細心觀察就會發現, 本來滿以為用來實現查詢機制的那個類Searcher, 其實和檢索機制幾乎毫無瓜葛, 就算有, 也不過是調用各參數的方法, 把這個作為那個的參數, 再把那個傳給另外的某某. 諸如此類. Searcher是個指揮家, 負責調度各功能使之協調, 用OO的話說, 就是對象間的協作. 各對象的功能被Searcher的操作過程利用了, 至於這些功能如何?, Searcher全然不知.
OO的一大強項是商務邏輯層面的簡潔表達, 清晰的語義甚至可以用優雅來形容. 這可以視為高內聚低耦合設計的副產品. Searcher.search()方法描述的邏輯是, Query把滿足查詢條件的文檔ID結果放到Scorer中, Scorer把結果塞進Collector. 這裡還有個暗示的語義: 無論輸入順序是什麼Collector中的結果總是按照score排序的. 從上述語義出發, 很容易分辨出職責的內聚以及類間的耦合關係.
具體考察一下TermQuery, 才更好理解內聚和耦合度. 在TermQuery的查詢中, 對文檔是否符合Term條件的判斷由TermQuery負責, 而評分則由TermScorer負責. 單一職責原則SRP體現得非常明顯, 而正是由於SRP被滿足, Query/Scorer的耦合才得以解開.
設計的過程中, 職責內聚和抽象往往是同時完成. 所以在瞭解了Query/Scorer的職責後, Lucene的類結構顯示出另一個十分重要的特性: 這就是開頭提到的, Searcher與Query的查詢過程無關. Searcher所要面對的查詢是無類型的, 它需要處理各種查詢. 查詢的共同外部特徵(在這裡就是Searcher能訪問的那些成員)抽象成了Query介面, Searcher與Scorer的關係也是這樣. 由於使用了物件導向技術中至關重要的多態性, Searcher只面對介面(依賴倒置原則DIP), 這樣就把查詢的商務邏輯同各查詢類型的演算法邏輯有效隔離開(解耦合)了. 這是職責內聚的結果, 同時也是把"查詢"概念高度抽象的結果.
於是我們看到Searcher實現了針對"查詢"的商務邏輯的描述, 對任何種類的查詢, 這一商務程序都適用(抽象), 而如何?查詢的演算法(結果提取和評分), 則是Query/Searcher各實作類別負責(內聚). 這麼做的好處不言而喻——OCP, 對擴充來說, Lucene是完全開放的, 而對更改則是封閉因而是安全的.
設想現在需要查詢連續出現的Term, 單個TermQuery當然不夠用, 檢索演算法已經變了. 但我們不需要改變現存任何一個類, 只要構建新的Query/Scorer實作類別, 比如PhraseQuery/Scorer. 目前Lucene 1.4.3版已經提供這樣一個檢索類型.
當然, Lucene是OOD方法的傑作, 不止上述幾點, 在各個實現層次上都能找到類似的設計痕迹, 這為整個軟體包的擴充和應用提供了強大的架構保證. 相信這也是Lucene得以流行的深層原因.
上一節
下一節