有些畫室是租來的,牆是房東的牆,樑柱是房東的樑柱,你不能在上面隨便釘釘子、鑿洞掛燈
但畫室要正常運作,總需要掛畫架、放顏料架、裝一盞夠亮的燈
聰明的畫家不會去敲掉房東的牆,而是自己打造一組可以移動的木架,靠在牆邊
需要的功能,全部長在木架上,牆一根釘子都不用動
ShippingFeeJob(Day 10)算完運費之後,團隊想加一個「預計送達日」的功能:
從今天起算三個工作天,如果算到週末,要順延到下一個工作天
DateTime 是 .NET 內建的型別,我們沒辦法在它身上加一個 IsWeekend() 方法,它的原始碼不歸我們管
最直覺的做法,是寫一個工具類別:
public static class DateUtils
{
public static bool IsWeekend(DateTime date) =>
date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
public static DateTime AddBusinessDays(DateTime date, int days)
{
var result = date;
while (days > 0)
{
result = result.AddDays(1);
if (!IsWeekend(result))
days--;
}
return result;
}
}
呼叫端用起來是這樣:
var estimatedDelivery = DateUtils.AddBusinessDays(DateTime.Today, 3);
if (DateUtils.IsWeekend(estimatedDelivery))
{
Console.WriteLine("送達日不應該落在週末");
}
DateUtils 能動,測試也能過,但它帶來幾個隱性代價:
DateTime 物件本身,看不出自己還有這些「隱藏能力」
DateTime,只能靠 IntelliSense 看到內建方法,根本不會知道專案裡還有一個 DateUtils.IsWeekend() 可以用DateUtils.IsWeekend(estimatedDelivery),主詞和動作被拆開了,明明是在問「這個日期是不是週末」,語感上卻像是「請 DateUtils 去處理這個日期」DateUtils 已經存在,很可能會在別的檔案裡,重新寫一個一模一樣的 IsWeekend 判斷問題不在於寫了一個工具類別,而是把「日期該有的行為」,硬釘在了一面不屬於自己的牆上
牆撐得住,但釘子看起來,永遠是外來的
C# 提供了一個專門為這種情境設計的語法,擴充方法 (Extension Method)
換成畫室的比喻:不動房東的牆,做一組木架,讓它靠在牆邊,看起來就像是牆的一部分
public static class DateTimeExtensions
{
public static bool IsWeekend(this DateTime date) =>
date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
public static DateTime AddBusinessDays(this DateTime date, int days)
{
var result = date;
while (days > 0)
{
result = result.AddDays(1);
if (!result.IsWeekend())
days--;
}
return result;
}
}
關鍵只在參數前面的 this。加了這個字,IsWeekend 就像是長在 DateTime 身上的原生方法
呼叫端變成:
var estimatedDelivery = DateTime.Today.AddBusinessDays(3);
if (estimatedDelivery.IsWeekend())
{
Console.WriteLine("送達日不應該落在週末");
}
讀起來,跟呼叫 DateTime 原生的 AddDays() 沒有任何差別
新人打出 estimatedDelivery. 的時候,IntelliSense 會自動列出 IsWeekend()、AddBusinessDays(),就像它們一直都在那裡一樣
不是所有「函式庫不夠用」的情況,擴充方法都能解決
DateTime 這個案例就很適合擴充方法讓生活變輕鬆,但它不是沒有代價:
如果哪天函式庫升級,內部行為改變了,你的擴充方法可能悄悄跟著失準,卻不會有任何編譯錯誤提醒你
多加的每一個擴充方法,都是團隊要長期維護的資產,不是免費的午餐
模組一蓋起了圓頂,模組二找到了透視法,明天我們要去看西斯汀教堂的天頂
米開朗基羅在灰泥乾掉之前,只有幾個小時可以決定筆觸。
時間壓力下的程式碼,會長出另一種完全不同的壞味道
模組三,正式開工