2012-07-25 73 views
1

更多地瞭解代碼合同,他們看起來像我想在我的項目中使用的東西。代碼合同和接口實現 - 什麼是1-1關係的原因

鑑於我構建Web服務層(從我們的MVC控制器訪問服務層的抽象)的方式,我發現自己想知道爲什麼無法爲接口的各種排列指定不同的協定實現。

具體而言,我對背後的原因感興趣,只能通過一種通用接口方法以1-1方式指定合同。

這裏是我的代碼結構的一個例子,我的目標是確定我想如何使用代碼合約。我相信有更多經驗的人能夠將我推向正確的方向。

我使用CQRS式的方法使得:

public interface IQuery<in TInput input,out TOutput output> 
{ 
    TOutput Invoke(TInput request) 
} 

public interface IGetSomeUnicornsFromAMagicalLand : 
              IQuery<int, IEnumerable<Unicorn>>{} 

// Implementation 
public class GetSomeUnicornsFromMagicLand : IGetSomeUnicornsFromAMagicalLand 
{ 
    public IEnumerable<Unicorn> Invoke(int numberOfUnicornsToReturn) 
    { 
     // Here I'd like to specify some preconditions on the input, 
     // specific to type int 

     return _wizardry 
       .GetMagicCreature<Unicorn>(numberOfUnicornsToReturn) 
       .DoMagicalConversionToEnumerable() 
    } 

} 

鑑於這種背景下,有理由要指定在執行層面的合同,而不是設計的接口上應用合同抽象類(作爲一種廣義機制)。

  • 不能做到這一點的原因是什麼?
  • 是否有其他方法可以滿足這個需求?
  • 如果我想使用代碼合約,這只是不是一個好的結構?
+0

你是指服務合同還是代碼合同?他們是非常不同的東西。WCF和你的例子讓我想到服務合同 – 2012-07-25 08:41:16

+0

我在引用'System.Diagnostics.Contracts' – 2012-07-25 08:42:33

回答

2

根據Liskov Substitution Principle,總的來說,我認爲在接口級別指定這些合約確實更有意義。

LSP基本上說抽象概念的每個實現應該是可以互換的,而抽象概念的用戶不必知道差異。換句話說,您的代碼不應該開始中斷,因爲您切換到某個接口的另一個實現。

通過對輸入參數強加特定於實現的附加要求,可以有效地將調用代碼(在您的案例中,很可能是框架代碼,處理IQuery的一般概念)耦合到特定實現。

爲了您的具體情況推理可能會或可能不適用(這就是爲什麼它是一個原則,它不是在石頭寫的),但顯然類似代碼合同的一般框架只能拿一般原則考慮:-)

編輯:通過一些更多的思考,在我看來,派生的接口可能是'真正的',而不是通用的IQuery<TInput, TOutput>。通用基礎接口似乎更像是一個「實現細節」,在這個通用基礎接口上實際上並不能真正做出任何有意義的假設(契約)。只有在具體的層面上,您才能對這些輸入和輸出參數做出任何有意義的推理。也許我們需要在C#中使用C++的私有繼承(或實現繼承)來從消費者'隱藏'這個接口。 Read this excellent answer to a related question。它還談到了在這種情況下Liskov可替代性是「謊言」,我傾向於同意。

+0

感謝您的回覆@jeroenh。我不斷回來的一個問題是確定如何使用代碼合同與泛型。如果我傳入一個int,我想要一個不同的聯繫人,如果我傳入一個字符串或自定義類型。如何在界面層面處理? – 2012-07-30 10:58:00

+0

@JamieDixon似乎是我的編輯。將合同放在通用基礎接口上可能沒有意義。 – jeroenh 2012-07-30 12:12:55

相關問題