「抽象類能在不破壞API修改」的剛性。
一旦類庫的版本(比如1.0.0.0)被提供給聚會,當我設計另一個版本(比如1.1.0.0)和修改時,它不會破壞代碼嗎?
你能舉出一個非常簡單的例子,它怎麼可能?
「抽象類能在不破壞API修改」的剛性。
一旦類庫的版本(比如1.0.0.0)被提供給聚會,當我設計另一個版本(比如1.1.0.0)和修改時,它不會破壞代碼嗎?
你能舉出一個非常簡單的例子,它怎麼可能?
抽象類和接口(在較小程度上)都是我們認爲的合同。抽象類可能比接口更復雜,因爲它們可以具有實現以及契約定義。這兩種類型都可以在沒有的情況下通過幾種方式打破合同(API)進行修改。有三種基本類型的合同變更:
在C#中,成員可以是方法,屬性,索引和字段。最簡單的,也是第一次沒有突破的變化是成員的增加。添加成員會增強API,但絕不會更改先前存在的API。刪除成員是一個突破性的變化,因爲之前的API在成員被刪除時確實發生了變化。
最終的選項,成員的修改,可能會或可能不一定會打破在C#中。在字段的情況下,唯一的修改是重命名。重命名公共領域總是一個突破性的改變。屬性可以重命名,或者他們可以添加或刪除setter/getter。增加一個setter/getter不會中斷,但所有其他屬性更改都會中斷。索引器和方法可以通過在現有參數列表末尾添加params參數來更改而不會破壞合同。對索引器和方法的任何其他更改也將是重大更改。
除了API級別之外,還應該考慮行爲更改。雖然我們應該始終努力保持API和行爲儘可能的分離,但並不總是如此。在創建新版本時,將重要的行爲細微差別及其對使用API的影響考慮在內。這些細微差別可能是方法拋出的異常,API成員對其他API成員的使用等。
一旦您瞭解了三種變化以及它們如何影響合同,您應該能夠更好地控制版本你的抽象類和接口。非中斷更改通常標有小版本更改,或者也許只是修訂更改。重大更改常常標有主版本更改。如果您採取謹慎的方式進行版本控制,它應該是一個非常易於管理的問題......只需確保在進行重大更改之前充分了解其影響。
在這些術語中,我將API理解爲客戶端代碼在使用類的版本(1.0.0.0)時可以使用的合同(公共方法定義的集合)。不是「破壞API」,只有在新版本的抽象類(1.1.0.0)中,您定義的新方法是非抽象的纔可能。任何在版本1.1.0.0中實現的爲摘要的新方法都將「破壞API」。 (另外,改變非抽象的方法定義將會「破壞API」)。
我認爲這個聲明意味着抽象類中的方法體可以被改變 - 而不需要改變接口。
考慮到這一點:
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進行編譯,他不必更改其代碼。
首先,我會說這不是特定於抽象類,而是一般類。
考慮下面的類:
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的例子與該類是否抽象無關。
有點深奧,但我們一直在這個 - 如果你的程序集是強命名幷包含配置數據,你可以通過更改版本號來破壞代碼。除非升級程序集時升級app | web.config,否則如果使用完整綁定路徑(比如說引用某個類型),則新程序集將無法加載。
更傳統的答案可能是您修正了抽象類中的錯誤,而無需更改任何成員。
A version policy也被推薦,但它需要團隊範圍採用才能工作。
在不破壞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
。 當然,您可以進行修改,使語法和語義保持不變,但這是您在製作新版本時總是要做的事情,並且存在許多不同的技術。它不是特定於基類,無論它們是否抽象。