畫室裡有一種工具,叫調色盤
它的角色,就是被動地裝顏料
畫家把紅色、黃色擠上去,自己動手調出想要的橘色調色盤本身不會調色,這很正常,它本來就只是個容器
但如果一個「應該懂得調色」的角色,卻只會被動地被人擠顏料、自己什麼判斷都不做
問題就不在調色盤,是有人把該做判斷的工作,丟給了不該做判斷的東西
專案裡有一個庫存記錄類別:
public class ProductStock
{
public string Sku { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
單看這個類別,完全看不出它在系統裡扮演什麼角色
它只是一袋被公開的欄位,任何人都可以把它塞滿、掏空、改到面目全非,ProductStock 自己完全不會有意見
真正「懂得」這筆庫存資料該怎麼用的邏輯,被放進了另一個類別:
public class InventoryValuationService
{
public decimal CalculateLineValue(ProductStock stock)
{
return stock.Quantity * stock.UnitPrice;
}
public bool IsLowStock(ProductStock stock)
{
return stock.Quantity < 10;
}
}
這兩個方法,做的事情都只跟 ProductStock 自己的欄位有關,卻被寫在別的類別裡
隨著專案長大,越來越多地方需要「算庫存價值」「判斷是否低庫存」
< 5,跟 InventoryValuationService 的 < 10 對不上
ProductStock就像那塊調色盤,誰都能往上面擠顏料
卻沒有人問過它:「這幾種顏色,你覺得該怎麼調?」它從來沒有機會回答,因為它根本沒有被賦予判斷的能力
解法是搬移方法 (Move Method):把只用到 ProductStock 自身欄位的邏輯,搬回 ProductStock 內部
public class ProductStock
{
public string Sku { get; }
public int Quantity { get; }
public decimal UnitPrice { get; }
public ProductStock(string sku, int quantity, decimal unitPrice)
{
Sku = sku;
Quantity = quantity;
UnitPrice = unitPrice;
}
public decimal CalculateValue() => Quantity * UnitPrice;
public bool IsLowStock() => Quantity < 10;
}
欄位也順手改成唯讀 (get 沒有 set),只能透過建構子設定一次
InventoryValuationService 不再需要存在
它原本存在的唯一理由,是幫 ProductStock 做它自己該做的判斷
報表模組、補貨提醒功能,全部改成直接呼叫 stock.CalculateValue()、stock.IsLowStock()
低庫存的門檻,終於只剩一個地方能改
節制美學提醒我們:判斷一個東西該不該留,要看它存在的角色,不是看它的外型
有幾種「純資料類別」的外型,其實是合理的:
Email、ProductId,雖然欄位不多,但它們有自己的驗證邏輯與行為,不是純資料類別,是被誤會成純資料類別的值物件
真正該被盯上的,是那種「明明有專屬於自己的判斷邏輯,卻被迫外包給別人」的資料容器
如果答案都是肯定的,這份行為,本來就該屬於它
明天我們看畫室角落一個更明顯的贅肉:一段沒有人再呼叫、卻也沒有人敢動手拆掉的舊程式碼
模組四第五站:無用的程式碼(Dead Code)