2011-08-27 53 views
2

我對使用具體類和接口的影響有一些疑問。OO原則:c#:設計爲接口而不是具體類

  1. 說的代碼(稱之爲chunkCode)一些大塊使用具體類A。我是否需要重新編譯chunkCode如果:

    1. 我在A中添加了一些新的公共方法?如果是這樣,那是不是有點道理?畢竟我仍然提供接口chunkCode依靠。 (或者我必須重新編譯,因爲chunkCode可能永遠不會知道這是真的,我沒有省略一些API)
    2. 我添加一些新的私有方法到A
    3. 我爲A添加了一個新的公共字段?
    4. 我添加一個新的私人領域A
  2. 工廠設計模式: 主代碼並不在乎對象的具體類型是什麼。它僅依賴於API。但是如果只有幾種方法只與一種具體類型相關,你會怎麼做?這種類型實現了接口,但增加了更多的公共方法?你會使用一些if (A is type1)語句(或類似的)的主要代碼?

感謝任何澄清

+0

你可以添加一些示例代碼?一般來說,如果一個接口沒有改變,你將不需要重新編譯,但是如果你正在對付具體的類,你會的。 – davecoulter

+1

哈,大家都在編輯這篇文章。我更喜歡項目列表... – celavek

回答

0

關於第一個問題的答案是否定的所有點。如果是這樣,那麼向後兼容性就沒有任何意義。只有在制動API時,你必須重新編譯chunkCode,即刪除chunkCode正在使用的某些功能,更改調用約定,修改參數數量,這些事情==打破更改。

對於第二個我通常,但只有當我真的必須在這些情況下使用dynamic_cast。我的答案在C++環境中是有效的;我剛剛看到問題是語言不可知(在這個小時裏有一種疲倦;如果它攻擊任何人,我會刪除答案)。

+0

你會如何迴應0verbose的答案? 「如果chunkCode直接引用一個具體類是」 –

+1

@Elad Benda在C++的情況下,我認爲我的答案是成立的。就Java而言,它也適用 - 在Java中,即使在添加到接口的情況下更改接口,也不必重新編譯(請參閱http://stackoverflow.com/questions/6780899/do-clients -have-to-recompile-after-changing-a-java-interface;在接受的答案中提供的鏈接是一個很好的閱讀)。有時候,在邊緣情況下,你確實需要重新編譯,參見http://stackoverflow.com/questions/536971/do-i-have-to-recompile-my-application-when-i-upgrade-a-third-party -罐。 – celavek

0

問題1:取決於你在說什麼語言。它總是比較安全的重新編譯兩種語言。主要是因爲chuckCode不知道A中實際存在的內容。重新編譯刷新其內存。但它應該在沒有重新編譯的情況下在Java中工作。

問題2:編號寫一個工廠的整個要點是擺脫如果(A是type1)。從維護的角度來看,這些陳述是可怕的。

工廠的目的是建立類似的對象。如果您遇到了使用此語句的情況,那麼該對象要麼與類的其餘部分不是類似的類型。如果您確定其類型相似,並且界面相似。我會在所有具體的基類中編寫一個額外的函數,並只在這個函數中實現它。

理想情況所有這些具體類都應該有一個共同的抽象基類或一個接口來定義API的內容。除非您正在編寫採用此特定類的函數,否則除了此接口中設計的內容外,應該預期在代碼中的任何位置調用該函數。

+0

「重新編譯刷新其內存」。重新編譯可能意味着清理上次編譯的輸出並重新編譯,而不是刷新內存。 –

+0

我不是指物理內存。我的意思是它將重新建立班級和功能位置。如果界面改變,這是特別必要的。你至少得到一個編譯錯誤。 Java可能不是這樣,但如果你看看C++,函數的實際位置就存儲在代碼中。 – arunmur

4

1)編譯不是面向對象的一項活動。它是特定OO實現的細節。如果你想要一個特定的實現(例如Java)的答案,那麼你需要澄清。

一般來說,有人會說增加一個接口不會被認爲是一個重大改變,其他人說你發佈後你不能改變接口,你必須創建一個新的接口。

編輯:您指定了C#,因此請檢查this question regarding breaking changes in .Net。我不希望這樣做會造成傷害,所以我不會在這裏複製它。

2)人們經常破解他們的設計來做到這一點,但這是一個標誌,你有一個糟糕的設計。

很好的替代

  • 創建界面中的任何一種方法,使您可以調用自定義的行爲,但並不需要知道行爲是什麼。

  • 創建支持新方法的附加接口(和新工廠)。新接口不必繼承舊接口,但如果有意義(如果接口之間可以表示關係),則可以繼承舊接口。

  • 如果您的語言支持它,請使用Abstract Factory pattern,並在混凝土工廠中利用Covariant Return Types。如果您需要特定的派生類型,請接受一個具體的工廠而不是抽象的工廠。

不好的選擇(反模式):

  • 添加到,什麼也不做其他接口的方法導出歸類。

  • 在對派生類沒有意義的方法中拋出異常。

  • 向接口添加查詢方法,告訴用戶他們是否可以調用某個方法。

除非方法名稱是通用的,以至於用戶不會指望它做任何事情(如DoExtraProcessing),然後補充說,是無操作的最派生類的方法打破了由該接口定義的合同。

例如:有人調用bird.Fly()會期望它實際做某事。我們知道雞不能飛。因此,Chicken不是BirdBird不是Fly

添加查詢方法對此很糟糕。例如。在界面中添加boolean CanFly()方法或屬性。所以拋出異常。他們都沒有解決類型根本不可替代的事實。檢查Liskov Substitution Principle(LSP)。

+1

+1:很好的解釋 – Heisenbug

+0

上面列出的反模式的一個值得注意的例外是[Freezable Pattern](http://msdn.microsoft.com/zh-cn/library/ms602734.aspx)。在這種情況下,您需要確保您返回的接口/基類是可凍結的,並且工廠的文檔聲明它總是返回凍結或解凍的實例。 –

+0

1)我指的是c#。那麼誰是對的(關於C#):0verbose還是celavek? –