2009-10-03 83 views
1

我明白C# - 實際例子 - 抽象類

「抽象類能在不破壞API修改」的剛性。

一旦類庫的版本(比如1.0.0.0)被提供給聚會,當我設計另一個版本(比如1.1.0.0)和修改時,它不會破壞代碼嗎?

你能舉出一個非常簡單的例子,它怎麼可能?

回答

3

抽象類和接口(在較小程度上)都是我們認爲的合同。抽象類可能比接口更復雜,因爲它們可以具有實現以及契約定義。這兩種類型都可以在沒有的情況下通過幾種方式打破合同(API)進行修改。有三種基本類型的合同變更:

  1. 添加成員
  2. 刪除成員
  3. 修改成員

在C#中,成員可以是方法,屬性,索引和字段。最簡單的,也是第一次沒有突破的變化是成員的增加。添加成員會增強API,但絕不會更改先前存在的API。刪除成員是一個突破性的變化,因爲之前的API在成員被刪除時確實發生了變化。

最終的選項,成員的修改,可能會或可能不一定會打破在C#中。在字段的情況下,唯一的修改是重命名。重命名公共領域總是一個突破性的改變。屬性可以重命名,或者他們可以添加或刪除setter/getter。增加一個setter/getter不會中斷,但所有其他屬性更改都會中斷。索引器和方法可以通過在現有參數列表末尾添加params參數來更改而不會破壞合同。對索引器和方法的任何其他更改也將是重大更改。

除了API級別之外,還應該考慮行爲更改。雖然我們應該始終努力保持API和行爲儘可能的分離,但並不總是如此。在創建新版本時,將重要的行爲細微差別及其對使用API​​的影響考慮在內。這些細微差別可能是方法拋出的異常,API成員對其他API成員的使用等。

一旦您瞭解了三種變化以及它們如何影響合同,您應該能夠更好地控制版本你的抽象類和接口。非中斷更改通常標有小版本更改,或者也許只是修訂更改。重大更改常常標有主版本更改。如果您採取謹慎的方式進行版本控制,它應該是一個非常易於管理的問題......只需確保在進行重大更改之前充分了解其影響。

1

在這些術語中,我將API理解爲客戶端代碼在使用類的版本(1.0.0.0)時可以使用的合同(公共方法定義的集合)。不是「破壞API」,只有在新版本的抽象類(1.1.0.0)中,您定義的新方法是非抽象的纔可能。任何在版本1.1.0.0中實現的摘要的新方法都將「破壞API」。 (另外,改變非抽象的方法定義將會「破壞API」)。

1

我認爲這個聲明意味着抽象類中的方法體可以被改變 - 而不需要改變接口。

考慮到這一點:

public abstract class Animal 
{ 
    public virtual string Speak() 
    { 
     return "erm"; 
    } 
} 

以後如果您發現該動物是不說話erm,但在你的1.1.0.0版本來說ya,所以,你可以將代碼更改爲:

public abstract class Animal 
{ 
    public virtual string Speak() 
    { 
     return "ya"; 
    } 
} 

在這種情況下,如果您的客戶端使用您的程序集版本1.0.0.0繼承了其他類中的Animal,那麼爲了使用您的1.1.0.0進行編譯,他不必更改其代碼。

1

首先,我會說這不是特定於抽象類,而是一般類。

考慮下面的類:

public class SomeClass 
{ 
    public bool IsValid(string input) 
    { 
     return !string.IsNullOrEmpty(input); 
    } 
} 

它定義需要一個string並返回一個bool的方法。如果字符串爲空或空,它將返回false。現在,讓我們改變它:

public class SomeClass 
{ 
    public bool IsValid(string input) 
    { 
     return !string.IsNullOrEmpty(input); 
    } 

} 

在這種情況下,我們添加了一種新方法。 previus方法未觸及。此更改不會以任何方式影響使用該類的代碼。接下來的變化:

public class SomeClass 
{ 
    public bool IsValid(string input) 
    { 
     if (string.IsNullOrEmpty(input)) 
     { 
      return false; 
     } 

     return input.Length > 5; 
    } 

    public void SomeNewMethod() { } 
} 

現在我們已經改變了行爲IsValid。舊代碼仍然可以不加修改地進行編譯,但是一些輸入值的結果已經改變。這是一種突破性變革。接下來的變化:

public class SomeClass 
{ 
    public void IsValid(DateTime input) 
    { 
     // do something with the input 
    } 

    public void SomeNewMethod() { } 
} 

現在我們已經改變了簽名IsValid。這會導致調用代碼不能編譯。這是另一種突破性變化。

正如你所看到的,這些破壞API的例子與該類是否抽象無關。

1

有點深奧,但我們一直在這個 - 如果你的程序集是強命名幷包含配置數據,你可以通過更改版本號來破壞代碼。除非升級程序集時升級app | web.config,否則如果使用完整綁定路徑(比如說引用某個類型),則新程序集將無法加載。

更傳統的答案可能是您修正了抽象類中的錯誤,而無需更改任何成員。

A version policy也被推薦,但它需要團隊範圍採用才能工作。

1

在不破壞API的情況下,可以修改抽象類 。

這只是錯誤的,或者完全是誤導。 API不僅是類接口的語法方面,而且也是它的語義 - 即所描述的行爲的某種方法。

這裏是我的意思的例子:在未來的版本中,你可能有

// v1 
public abstract class A 
{ 
    void DoSomething() 
    { 
     ... 
     if (someCondition) 
     { 
      throw new SomeException(); 
     } 
    } 
} 

現在:

// v2 
public abstract class A 
{ 
    void DoSomething() 
    { 
     ... 
     if (someCondition) 
     { 
      throw new DifferentException(); 
     } 
    } 
} 

和你「API」 - 看似不變 - 可能是這樣的:

public class B: A 
{ 
    ... 
    void DoSomething(); // inherited from base 
} 

但實際上,當用v2替換基類v1時,您並沒有保留API不變,因爲可能有一些呼叫代碼依賴於SomeException而不是DifferentException。 當然,您可以進行修改,使語法和語義保持不變,但這是您在製作新版本時總是要做的事情,並且存在許多不同的技術。它不是特定於基類,無論它們是否抽象。