這個做法建議把型別的可見性降低,目標是能完成任務的最低可見性而不是把所有類別都設定為 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 你就不能輕易修改, 所以可見性越低未來反而更容易擴充與修改。