並不是所有的人都須要知道所有的事。也不是所有的類型須要是公共的。對於每個類型,在滿足功能的情況下,應該盡可能的限制訪問級別。而且這些訪問級別往往比你想像的要少得多。在一個私有類型上,所有的用戶都可以通過一個公共的接口來訪問這個接口所定義的功能。
讓我們回到最根本的情況上來:強大的工具和懶惰的開發人員。VS.net對於他們來說是一個偉大的高產工具。我用VS.net或者C# Builder輕松的開發我所有的項目,因為它讓我更快的完成任務。其中一個加強的高產工具就是讓你只用點兩下按鈕,一個類就創建了,當然如果這正是我想要的話。VS.net為我們創建的類就是這樣的:
public class Class2
{
public Class2()
{
//
// TODO: Add constructor logic here
//
}
}
這是一個公共類,它在每個使用我的程序集的代碼塊上都是可見的。這樣的可見級別太高了,很多獨立存在的類都應該是內部(internal)的。你可以通過在已經存在的類裡嵌套一個受保護的或者私有的類來限制訪問。 越低的訪問級別,對於今後的更新整個系統的可能性就越少。越少的地方可以訪問到類型,在更新時就越少的地方要修改。
只暴露須要暴露的內容,應該通過嘗試在類上實現公共接口來減少可見內容。你應該可以在.Net框架庫裡發現使用Enumerator模式的例子,System.ArrayList包含一個私有類,ArrayListEnumerator, 而就是它只實現了IEnumerator接口:
// Example, not complete source
public class ArrayList: IEnumerable
{
private class ArraylistEnumerator : IEnumerator
{
// Contains specific implementation of
// MoveNext( ), Reset( ), and Current.
}
public IEnumerator GetEnumerator()
{
return new ArrayListEnumerator( this );
}
// other ArrayList members.
}
對於我們這樣的使用者來說,不須要知道ArrayListEnumerator類,所有你須要知道的,就是當我們在ArrayList對象上調用GetEnumerator函數時,你所得到的是一個實現了IEnumerator接口的對象。而具體的實現則是一個明確的類。.Net框架的設計者在另一個集合類中使用了同樣的模式:哈希表(Hashtable)包含一個私有的HashtableEnumerator, 隊列(Queue)包含一個QueueEnumerator, 等等。私有的枚舉類有更多的優勢。首先,ArrayList類可以完全取代實現IEnumerator的類型,而且你已經成為一個賢明的程序員了,不破壞任何內容。其實,枚舉器類不須要是CLS兼容的,因為它並不是公共的(參見原則30)。而它的公共接口是兼容的。你可以使用枚舉器而不用知道實現的類的任何細節問題。
創建內部的類是經常使用的用於限制類型可見范圍的概括方法。默認情況下,很多程序員都總是創建公共的類,從來不考慮其它方法。這是VS.net的事。我們應該取代這種不加思考的默認,我們應該仔細考慮你的類型會在哪些地方使用。它是所有用戶可見的?或者它主要只是在一個程序集內部使用?
通過使用接口來暴露功能,可以讓你更簡單的創建內部類,而不用限制它們在程序集外的使用(參見原則19)。類型應該是公共的呢?或者有更好的接口聚合來描述它的功能?內部類可以讓你用不同的版本來替換一個類,只要在它們實現了同樣的接口時。做為一個例子,考慮這個電話號碼驗證的問題:
public class PhoneValidator
{
public bool ValidateNumber( PhoneNumber ph )
{
// perform validation.
// Check for valid area code, exchange.
return true;
}
}
幾個月過後,這個類還是可以很好的工作。當你得到一個國際電話號碼的請求時,前面的這個PhoneValidator就失敗了。它只是針對US的電話號碼的。你仍然要對US電話號碼進行驗證,而現在,在安裝過程中還要對國際電話號碼進行驗證。與其粘貼額外的功能代碼到一個類中,還不如了斷減少兩個不同內容耦合的做法,直接創建一個接口來驗證電話號碼:
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創建一個公共的抽象類,它包含通用驗證的實現算法。用戶應該可以通過程序集的基類訪問公共的功能。在這個例子中,我更喜歡用公共接口,因為即使是同樣的功能,這個相對少一些。其他人可能更喜歡抽象類。不管用哪個方法實現,在程序集中盡可能少的公開類。
這些暴露在外的公共類和接口就是你的合約:你必須保留它們。越多混亂的接口暴露在外,將來你就越是多的直接受到限制。越少的公共類型暴露在外,將來就越是有更多的選擇來擴展或者修改任何的實現。
返回教程目錄