1

一位同事和我已經想出了一種編碼風格衝突。我們都開始與接口,說:實現接口與提供接口

interface IMessageList { 
    void AddMessage(IMessage message); 
    void RemoveMessage(IMessage message); 
} 

我將使用的接口的方式是,我將有一個類實現它:

// Method 1 
class SomeClass : IMessageList { 
    private IList<IMessage> messages = new List<IMessage>(); 
    public AddMessage(IMessage message) { 
     messages.Add(message); 
    } 
    public RemoveMessage(IMessage message) { 
     messages.Remove(message); 
    } 
} 

什麼我的同事會做,但是,這是:

// Mehod 2 
interface IMessageListProvider { 
    IMessageList MessageList { get; } 
} 
class MessageList : IMessageList { 
    // An implementation similar to the above 
} 
class SomeClass : IMessageListProvider { 
    private IMessageList messageList = new MessageList(); 
    public MessageList { get { return messageList; } } 
} 

他說,他得到了主意,從微軟這樣​​的代碼編程,但在我的研究,我發現,這聽起來像,這是「一個可插拔單的微軟特定形式的「供應商」模式, 「但我的同事從未真正使用過它。這就是他通常如何使用接口。另外,我發現一些人甚至在其正確的上下文中對提供者模式提出批評:http://blog.ploeh.dk/2011/04/27/ProviderIsNotAPattern.aspx,http://www.colourcoding.net/blog/archive/2009/07/29/microsofts-provider-pattern-a-bad-idea-carried-out-to-perfection.aspx。所以,撇開提供商模式本身的更具體的問題,這裏有利弊我只是上面的代碼中看到:

至於第一種方法:

優點:

  • 我們只定義和使用一個合約(IMessageList),而我們對IMessageList的使用不依賴任何其他抽象。
  • 它是擴展的,因爲可以很容易地覆蓋IMessageList成員在派生類型和經由base.AddMessage訪問基實施方式中,等

缺點:

  • 實現大量的接口的類這種方式最終會帶來很多方法,並且更容易出現名稱衝突。
  • 在許多類正在實現和轉發接口方法的情況下,將會有與接口中成員一樣多的轉發器。
  • SomeClass和其他組件之間沒有IMessageList相關組件之間的封裝。

至於第二種方法:

優點:

  • 在許多類實施和轉發IMessageListProvider案件,只有一個方法需要在每個被覆蓋。
  • IMessageList的實施保留在SomeClass以外,保持較寬鬆的耦合。

缺點:

  • 實現了很多供應商這種方式僅僅是隱藏的複雜性,而不是它封裝的。
  • IMessageList是我們定義的真實合約,但幾乎沒有任何代碼單獨使用IMessageList,因爲它總是需要經過IMessageListProvider。我們的抽象依賴於另一個抽象。
  • 我們永遠不可能同時實現IMessageListIMessageListProvider,因爲那樣會有兩種方法可以使用'IMessageList'方法,而其中一種方法可能不正確。這對我們定義的類型提出了限制,但實現這兩個接口不會導致任何編譯器錯誤。
  • 我們不能輕易覆蓋IMessageList的行爲,因爲調用base.MessageList只會給我們一個對象。相反,我們必須重寫它,以返回基類正在使用的任何對象的子類,並對其進行擴展。

在我的方法,如果我們覆蓋的IMessageList成員,那麼我們知道會發生什麼,因爲每個人都使用IMessageList。在他的方法中,我們需要爲使用IMessageListProvider的類放置一個抽象工廠,以便我們可以向它們發送實現我們想要的派生類型,但這似乎還有很長的路要走,以回到簡單的多態一個接口應該包含。此外,它似乎鎖定我們只使用一個實現IMessageList,因爲它很難覆蓋;但是,如果我們只有一個具體的實現,那麼爲什麼接口羣呢?我們從合同開始(IMessageList),但現在我們依賴於另一個抽象來使用它(IMessageListProvider),這很難被覆蓋,它基本上限制我們使用一種類型(MessageList)我們原來的抽象變成了一個結實體。

總之,我從他的方法中得到一種難聞的氣味,但他堅定地認爲兩者之間的選擇僅僅是一種風格差異,所以我不知道我是否錯過了一些東西。我做了很多搜索,看看我能否在這方面找到其他意見,但是我只提出了與提供者模式有關的事情,所以我不知道它們應用得如何。所以我很好奇社區如何看待這種提供接口作爲屬性獲取器與實際實現接口的一般風格。

作爲一種妥協,我的同事,我建議是這樣的:

// Compromise 
class SomeClass : IMessageList { 
    private struct MessageList : IMessageList { 
     private IList<IMessage> messages = new List<IMessage>(); 
     public AddMessage(IMessage message) { 
      messages.Add(message); 
     } 
     public RemoveMessage(IMessage message) { 
      messages.Remove(message); 
     } 
    } 
    private readonly messageList = new MessageList(); 
    public AddMessage(IMessage message) { 
     messageList.Add(message); 
    } 
    public RemoveMessage(IMessage message) { 
     messageList.Remove(message); 
    } 
} 

這實現了他SomeClass及其實施IMessageList之間要封裝,但沒有顛覆接口的方式上面。或者MessageList可以內部或公開,以便其他類可以使用它。但是我認爲MessageList應該總是作爲一種方便被轉發,並且從不簡單地返回,因爲它實際上成爲接口的實現,這破壞了我們的抽象。

回答

0

從我在這裏看到的,我不認爲需要IMessageListProvider,特別是如果您使用不吸引的依賴注入框架。具有xxxProvider的情況在「提供」對象根據提供者類中的邏輯而變化時出現。這裏有有效原因需要IMessageListProvider

  1. 構建或獲得IMessageList在運行時是昂貴的,它的建設,應儘可能晚地推遲。
  2. IMessageList的不同實現必須在運行時由IMessageListProvider實現中包含的邏輯來選擇。

從您的描述中,這些看起來都不是真的。這似乎是cargo cult programming的一個明確示例,並且強制使用者IMessageListProvider違反了Law of Demeter

+0

感謝您的回答,並感謝您的參與!即使我只想對我的問題說「tl; dr」。 – user875215