最近整理了一下公司規範,其中「函數的參數個數不宜超過4個」這一條在推廣的時候有些爭議,在能完成任務的情況下參數越少肯定越好,這本身沒什麼可爭議的,但這樣做可能會在程式設計時帶來困難,爭議點在是否值得這樣做。我覺得為了讓使用函數者更簡單這樣做還是值得,至於程式設計時的困難往往是因為我們不熟悉一些減少參數的方法,這裡總結幾條供大家參考:
1.使用結構來封裝參數
範例:新增用戶
原函數體:AddUser(string userName,string password,string address,string phone,int age)
重構:新增一個User類別:
class User
{
public string UserName { get; set; }
public string Password { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public int Age { get; set; }
}
將AddUser改為:AddUser(User user)
存在的問題:如果新增的這個類別沒有其他地方用,常常會覺得這樣做並不值得,這時我們可以考慮使用匿名類別封裝參數的方法。
2.使用屬性來替換參數
如果將1中的AddUser方法置於User類別中,那麼AddUser方法中的user參數都可以省掉了,有的時候可能會為了減少某些方法的參數個數而添加某些屬性。在物件導向設計中,物件應該自己負責自己,而且應該清楚定義責任。某個方法參數太多的原因可能是這個方法寫在了他不該存在的地方,GRASP原則中講的「資訊專家」模式很多情況下可以減少參數的個數。
例:帳戶轉帳
原函數:Transfer(Account from,Account to,decimal money)
重構:
程式碼
public class TransferProcess
{
private Account From;
private Account To;
public TransferProcess(Account from, Account to)
{
this.From = from;
this.To = to;
}
public void Transfer(decimal money)
{
if (money<From.Money)
{
From.Money = From.Money - money;
To.Money = To.Money + money;
//更新資料庫
}
else
{
throw new Exception("超出餘額");
}
}
}
註:資訊專家模式是物件導向設計的最基本原則,我們設計物件(類)的時候,如果某個類別擁有完成某個職責所需的所有訊息,那麼這個職責就應該分配給這個類別來實現。這時,這個類別就是相對於這個職責的資訊專家。
3.使用私有函數
呼叫某個函數時往往並不需要很多的交互參數,但我們提供參數時則需要提供所有的情況,這時我們可以把函數分類,將最複雜的函數封裝成私有的,而暴露出來的簡單函數呼叫這些複雜函數完成功能。讓我們來看看mvc中的TextBox方法的實作方式:
程式碼
public static string TextBox(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
return InputHelper(htmlHelper, InputType.Text, name, value, (value == null) /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
}
private static string InputHelper(this HtmlHelper htmlHelper, InputType inputType, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicit, IDictiontion, bool isChecked, bool setId, bool isExplicit, IDictiontion.
if (String.IsNullOrEmpty(name)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
}
TagBuilder tagBuilder = new TagBuilder("input");
... ...
但有時我們為了給呼叫者最大的彈性,可能也會把最複雜的那個函數重載露出來。
4、params 關鍵字
指定在參數數目可變處,採用參數的方法參數。
用法:
程式碼
static void Main(string[] args)
{
UseParams(1, 2, 3);
}
public static void UseParams(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.WriteLine(list[i]);
}
Console.WriteLine();
}
這種方法其實並沒有減少參數的個數,只是簡化了函數體。
5.使用匿名類別封裝參數
準備知識:我們先來看看RouteValueDictionary
程式碼
static void Main(string[] args)
{
RouteValueDictionary r = new RouteValueDictionary(new { id=1,name="lfm"});
foreach (var item in r)
{
Console.WriteLine("{0}:{1}", item.Key, item.Value);
}
//Console.WriteLine();
}
結果:
id:1
name:lfm
RouteValueDictionary可以將實例的屬性名稱及屬性值存入字典中。
mvc中很多地方都使用這種方式傳遞參數。
例如: <%= Html.ActionLink("Details", "Details", new { id=item.id })%>
ActionLink方法體中就是使用RouteValueDictionary將匿名物件進行了分解,然後將其拼裝到連結上。