這個做法建議把型別的可見性降低,目標是能完成任務的最低可見性而不是把所有類別都設定為 public。
建立一個 public 型別非常容易,你可以不加思索把型別都設定為 public 也能讓程式正常運作,但是越大的可見性會在更新的時候造成困擾, 因為任何一個地方都有可能呼叫到你的函式庫,如果修改成 protected、internal 或 private 對你的類別進行限制,保留最低可運作的 可見性,這樣就能確保在更新的時候只有一部分相干的程式需要更改。
首先在建立新型別時要先考慮型別要在哪裡使用,是否客戶會使用到所有的功能或者是只有同一個 assembly 裡面會呼叫到。
假設有一段驗證電話號碼的程式碼,它可以驗證美國的電話號碼。
public class PhoneValidator
{
public bool ValidateNumber(PhoneNumber ph)
{
// perform validation.
// Check for valid area code, exchange.
return true;
}
}
但之後追加須驗驗證國際號碼的需求,你或許可以把它都塞在 ValidateNumber 裡面,但建議是建立一個驗證號碼的介面,並分別實做兩個 internal 類別。
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;
}
}
之後透過建議的工廠模式來建立不同格式的驗證器,這樣就能把兩個 internal 類別隱藏起來,因為它們對呼叫方來說並不需要可見,
呼叫方想要的只是驗證電話號碼的這個動作,所以透過公開 IPhoneValidator
介面的 ValidateNumber
方法就足夠了。
public static IPhoneValidator CreateValidator(PhoneTypes type)
{
switch (type)
{
case PhoneTypes.UnitedStates:
return new USPhoneValidator();
case PhoneTypes.UnitedKingdom:
return new UKPhoneValidator();
case PhoneTypes.Unknown:
default:
return new InternationalPhoneValidator();
}
}
有更少的 public 型別,同時也代表更少的方法能夠給呼叫方使用,也代表你能少寫幾個檢驗證些公開方法的相關測試, 並且透過公開介面的方式,能夠更方便的使用單元測試的 mock 與 stub 功能。
Summary
這個做法建議限制方法與類別可見性,畢竟越多的可見性代表之後要改版時考慮的事情會更多,因為只要有使用者在使用某一個 API 你就不能輕易修改, 所以可見性越低未來反而更容易擴充與修改。