2014-01-18 168 views
0

最近我一直在進行面向對象的設計狂歡,努力提高設計技能。這個問題是關於我經常看到的一個特定的設計選擇,並且不理解其基本原理。我知道設計選擇傾向於主觀,但我想知道別人怎麼看待這個,以確定我的設計本能是否越來越好。我正在看Robert C Martin(Uncle Bob) -Clean Architecture and Design-2012 COHAA The Path to Agility Conference。在講話中,他講述了一個關於開發Fitnesse的故事。我對軟件不熟悉,所以我查找並找到the project hosted on github實現接口vs返回實現接口的對象的方法

在瀏覽項目時,有一件事在WikiPage interfacePageCrawler getPageCrawler();方法中引起了我的注意。所以我查了一下PageCrawler interface看看是什麼樣子。在檢查這個接口時,我認爲PageCrawler中的方法看起來像是屬於WikiPage,而WikiPage可以合理地實現接口。

我認爲將兩者分開可能會導致WikiPage公開內部消息,以便抓取該頁面所需的信息可供抓取它的對象訪問。而且,BaseWikiPage抽象類只是返回一個新的PageCrawlerImpl,並且從我所看到的項目中沒有其他PageCrawler實現。

我在其他項目中看到過這種類型的代碼,其中一個接口/類的方法返回另一個接口/類的對象的方法可以合理地屬於第一個類。在試圖看到Fitnesse開發人員的意圖時,我想出了這個設計的唯一原因是,通過實現WikiPage創建新wiki頁面的開發人員不需要重新實現爬網功能,即爬網功能無論維基頁面的實現如何,都應該是相同的。這是這種設計的目的,還是我錯過了什麼?

我在SO上發現了Implementing an interface vs. providing an interface問題,但它並不完全相同,也沒有給出很多關於何時可以設計這樣的東西的信息。

回答

2

你所看到的是界面分離原理的實際應用。你說PageCrawler的界面讓你認爲「這些都屬於WikiPage中的所有東西」,但這是從實現的角度來看事物,而不是有人打電話給WikiPage的觀點。

編輯: 也許是少計接口分離原則,更多的是單一職責原則。

+0

感謝喬恩,我沒有這麼想。但是,我對ISP的解釋是不同的。我認爲ISP將責任分離爲內聚接口並通過實現接口來組成對象,而不一定從實現接口的方法返回對象。在這種情況下,我認爲'PageCrawler'接口本身的存在就是使用ISP。如果'WikiPage'實現它,'PageCrawler'接口的客戶端仍然不知道實現者是'WikiPage'。 – TheSecretSquad

+0

此外,聽起來好像你說'WikiPage'的客戶端不應該瞭解'PageCrawler'方法(這是你的意思?),但他們仍然可以訪問他們,除非他們依賴於似乎一個多餘的消息('getPageCrawler')。如果使用裝飾器,這會不會更好,例如,使用'WikiPage'實現'PageCrawler',然後'WikiPage'可以選擇委託給一個內部也實現'PageCrawler'的單獨對象?我想我的觀點是:他們說'WikiPage'有一個'PageCrawler',但對我來說'WikiPage'是_crawlable_。 – TheSecretSquad

1

在某些情況下,某些與對象「關聯」的函數將需要保持應與對象本身分離的狀態。在.NET中,IEnumerator<T>方法就是這方面的主要例子。它們的含義通常來自於相關聯的IEnumerable<T>,但是每個枚舉器都具有應該與底層集合的狀態分開的狀態(尤其是因爲典型的實施方式IEnumerable<T>將無法​​知道多少個具有獨立狀態的枚舉器可能。可與它同時

相關分束斷接口實現的幾個更多的優點:

  • 的拆過實現可包括構件,其名稱反映那些基礎類型的,但其功能是不同的。例如,Dictionary<TKey,TValue>實施IEnumerable<KeyValuePair<TKey,TValue>>,但具有執行IEnumerable<TKey>Keys屬性。 DictionaryKeys返回的東西都有一個GetEnumerator方法,但是枚舉鍵值對,而另一個枚舉鍵。

  • 如果拆分實現包裝底層對象,並且不公開對其的引用但公開其某些功能,則它可以安全地暴露給受限於有限功能的可信代碼,但不能與不受約束的對象引用。在某些情況下,擁有一個對象來保存對單個包裝對象的引用(可以用來滿足任意數量的請求)可能比客戶使用包裝對象的構造函數爲每個包裝對象創建一個新的包裝對象請求。

  • 儘管Java和.NET都不允許雙重繼承,但是每個緊密關聯的對象都有可能從不同的類繼承。

我不熟悉你提到的特定類型,所以我不知道他們的行爲特殊原因,但提到的原因都是常見的。