有些時候你可能會注意到方法參數很多,每次要使用時都得花一些時間確認方法參數有哪些,參數順序又是什麼,導致沒辦法很流暢的使用該方法,必須一點時間讓畫面反覆在使用端和方法間切換並確認,這也是代碼壞味道之一:過長參數
當方法參數過長的時候,可以透幾個方法來改善
共有的參數放到類別欄位中
例如在下面這個例子中,單看這個方法會發現有三個參數,在方法中也是每個參數都會使用到。
private CarpakPoint StepsToLastLevel(int[,] carpark, CarpakPoint nextStart, List<string> result)
{
...
}
但是如果再觀察類別中的其他方法,就會發現幾乎每個方法都需要carpark且不會對carkpark進行修改,再者類別名稱也是Carpark與有絕對carpark參數的相關而不是Helper之類的類別。
public class CarPark
{
...
public string[] Escape(int[,] carpark)
{
...
}
private CarpakPoint StepsToLastLevel(int[,] carpark, CarpakPoint nextStart, List<string> result)
{
...
}
private CarpakPoint FindStart(int[,] carPark)
{
...
}
private int FindStart(int[,] carPark, int level)
{
...
}
private string CalculateToExit(int startIdx)
{
...
}
}
在這種情況下,我們可以把carkPark參數放心的放到類別欄位中,讓每個方法可以直接讀取carpark,而不需要透過方法參數一層一層傳下去。
拆解方法職責
假設我們在設計一個使用者登入系統,當使用者登入時,Login(LoginRequest request)會被呼叫,並透過呼叫遠端_loginApi來完成登入。不難發現DoLogin的參數多達五個,如果其他人需要閱讀或重複使用這個方法時,就會變得不易使用。
public void Login(LoginRequest request, UserStatus status, string country)
{
....
TryLogin(request.UserId, request.Password, status.IsSuspend, status.IsVip, country);
...
}
private void TryLogin(int userId, string password, bool isSuspend, bool isVip, string country)
{
if (isSuspend)
{
throw new AccountSuspendException();
}
else if (isVip)
{
_loginApi.VipLogin(userId, password, country);
}
else
{
_loginApi.Login(userId, password, country);
}
}
如果仔細觀察一下,DoLogin的職責包含確認使用者是否可以登入與呼叫遠端API執行登入。此時我們應該讓DoLogin只包含一個職責,並把另一個職責拆分出去。
public void Login(LoginRequest request, UserStatus status, string country)
{
...
CheckUserCanLogin(status.IsSuspend);
TryLogin(request.UserId, request.Password, status.IsSuspend, status.IsVip, country);
...
}
private void CheckUserCanLogin(bool isSuspend)
{
if (isSuspend)
{
throw new AccountSuspendException();
}
}
private void TryLogin(int userId, string password, bool isVip, string country)
{
if (isVip)
{
_loginApi.VipLogin(userId, password, country);
}
else
{
_loginApi.Login(userId, password, country);
}
}
Parameter Object
在上面的例子中,可以發現雖然已經少了一個參數,但是參數數量還是有四個,但是這些都是必要的參數。此時我們可以把這些參數放到一個簡單的DTO中,作為執行Login操作的唯一參數。
public class LoginParameters
{
public int UserId { get; set; }
public string Password { get; set; }
public bool IsVip { get; set; }
public string Country { get; set; }
}
public class LoginService
{
...
private void TryLogin(LoginParameters loginParameters)
{
...
}
...
}
這種做法除了DoLogin的參數長度之外,還能讓參數設定更為自由,一些驗證參數數值是否合法的行為也都能放在這個參數列別中。除此之外,相比從方法參數從左向右一個一個看,從類別屬性可以更快知道這個方法需要哪些參數。
雖然解決參數過長的方法有不少,但都還是得依實際情況來選擇,有三個方法都會用上,有時甚至可能出現上面三個方法都不適用,所以除了發現參數過長的問題之外,還需要透過思考來判斷現在是因為什麼原因導致的,然後選擇較適合的才是有效的作法。