[Effective C# 4.0 譯] 條款21:限定類型的可見度

來源:互聯網
上載者:User

[Effective C# 4.0 譯] 條款21:限定類型的可見度

翻譯:羅朝輝(http://www.cnblogs.com/kesalin/)

 

由於調查不到位,[Effective C#]C# 4.0版本的中文版業已出版,書名為《C#高效編程》,翻譯系列不會繼續了,大約會寫些讀書筆記吧。

題記:網路中已不乏[Effective C#]的中文翻譯版,中文版本也已出版,但是內容比較老,不是最新版(C# 4.0),這就是我翻譯該系統文章的原因之一;本人雖然胡亂碼過幾年C/C++,Java,Objective-C,但卻是C#新手,一邊翻譯一邊學習是我翻譯該系列文章的原因之二。因為是新手,錯誤疏落難免,還請各位指正。著作權申明:[Effective C# 4.0 譯]系列翻譯文章僅為學習愛好之用,遵循“署名-非商業用途-保持一致”創作公用協議,請支援英文正版。


條款21:限定類型的可見度

並非所有人都需要知道所有事。也並非你建立的所以類型都需為public。你應只賦予每個類型用來完成你工作所必須的最小的可見度,通常比你能想象的還要少。內部或私人類型能實現public介面,所有客戶都可以訪問由在私人類型中聲明的public介面定義的功能

建立public類型實在是太容易了,並且,通常那樣做也是適宜的。許多獨立存在的類都應該是內部的,還可以在類中建立protected或private嵌套類來進一步限制其可見度。一個類的可見度越少,在對整個系統更新時所須做的改動就越少;可訪問一段代碼的地方越少,在對之進行修改時所須做的改動也就越少。

只暴露必須被暴露的內容,盡量通過實現public介面來減少類的可見度。可以在.NET 架構庫中找到使用迭代模式的例子,System.Collections.Generic .List<T>包含一個私人類:Enumerator<T>,它實現了IEnumerator<T>介面:

// For illustration, not complete source
public class List<T> : IEnumerable<T>
{
private class Enumerator<T> : IEnumerator<T>
{
// Contains specific implementation of
// MoveNext(), Reset(), and Current.
public Enumerator(List<T> storage)
{
// elided
}
}

public IEnumerator<T> GetEnumerator()
{
return new Enumerator<T>(this);
}
// other List members.
}


像我們這樣的調用者,從不需要知道Enumerator<T>類的存在。我們只須知道當我們在 List<T>對象上調用GetEnumerator方法時所得到的是一個實現了IEnumerator<T>介面的對象就可以了。.NET架構的設計者們在實現其他集合類時也遵循了同樣的模式:Dictionary<T>包含一個私人的DictionaryEnumerator<T>類;Queue<T>包含一個私人類QueueEnumerator<T>;等等。私人的枚舉類有很多好處,首先,List<T>完全可以取代實現IEnumerator<T>介面的類型,而你不會破壞任何東西,哇,你已是一個聰明的程式員了;其次,枚舉類無須是CLS(Common Language Specification)相容的,因為它不是public(參考條款 49),而它的public介面是相容的。你可以使用列舉程式而不用知道實現該列舉程式的類的細節。

建立內部類是一種常被忽視的限制類範圍的手段。預設情況下,很多程式員總是建立public類,而不考慮其他替代方案。那正是VS .NET嚮導所做的事情。你應該仔細考慮新的類型會在什麼情況下使用,而不是不假思索地接受預設方案。類型對所有客戶都有用嗎?或它主要是在當前程式集內部使用?

通過使用介面來暴露功能,可使你更容易地建立內部類,而不會在程式集外部限制其可使用性(參考條款 26)。類型需要為public嗎?或彙總一組介面來描述其功能是更好方式?使用內部類你可以用不同版本的類來替換,只要這些類實現同樣的介面。舉例來說,考慮一個驗證電話號碼的類:

public class PhoneValidator
{
public bool ValidateNumber(PhoneNumber ph)
{
// perform validation.
// Check for valid area code, exchange.
return true;
}
}

這個類很好地工作了好幾個月。這時你得到一個處理國際電話號碼的需求,先前實現的PhoneValidator不能正常工作了,因為它只處理美國電話號碼。現在你仍需驗證美國電話號碼,只不過你需要使用能處理國際號碼版本的一攬子解決方案。與其在一個類中耦合額外的功能,不如在兩個不同的類中將之解耦。你建立一個介面來驗證任意電話號碼。

public interface IPhoneValidator
{
bool ValidateNumber(PhoneNumber ph);
}

 

接著,修改已有的電話驗證邏輯來實現這個介面,並將它當做一個內部類:

internal class USPhoneValidator : IPhoneValidator
{
public bool ValidateNumber(PhoneNumber ph)
{
// perform validation.
// Check for valid area code, exchange.
return true;
}
}

 

最後,你可以建立一個類來處理國際電話號碼:

internal class InternationalPhoneValidator : IPhoneValidator
{
public bool ValidateNumber(PhoneNumber ph)
{
// perform validation.
// Check international code.
// Check specific phone number rules.
return true;
}
}

 

為了完成這個實現,你需要建立基於電話號碼類型的合適的類。你可以使用原廠模式來實現。在程式集外部,只有介面是可見的。那些用於驗證特定地區號碼的類,只在程式集內部可見。你可以為不同地區增加驗證類而不會影響系統中的其他程式集。通過限制類的範圍,你就限定了升級或擴充整個系統時所需修改的代碼。

你還可以為PhoneValidator建立一個public 抽象基類,它包含通用的驗證演算法邏輯。客戶能夠通過可訪問的基類來訪問公用功能。在這個例子中,我更喜歡使用public 介面的實現,因為它只含有很少的公用功能。其他使用者可能更偏好public抽象基類。無論選擇哪一種方式實現,都只有很少類具有public訪問性。

此外,public類型越少,暴露的public地區就越少,從而更容易進行單元測試覆蓋。如果只有很少的public類型,那你需要測試的可訪問的public方法也就越少。同樣,如果通過介面暴露的public API越多,你就自動建立了一個系統,藉助該系統你能夠用某些用於單元測試目的存根來取代這些類型。

那些公開暴露給外部世界的類和介面就是你的合約:你必須實現它。介面越混亂,前途就越渺茫。暴露的介面越少,將來你就有更多的選擇來擴充或修改任何實現。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.