依戀情結,是學徒天天跑去隔壁幫忙
不適當的親密關係,是更嚴重的下一步
兩間畫室,乾脆共用一把鑰匙,誰都能隨時進出對方的工作間
翻看對方還沒完成的草稿、隨手調整對方的顏料配方表面上,兩間畫室還是各自獨立的招牌,實際上,早就分不清誰的作品是誰畫的
動一間畫室的規矩,另一間也會跟著亂
購物車類別本身很單純:
public class Cart
{
public int Qty { get; set; }
public decimal UnitPrice { get; set; }
public string DeliveryMethod { get; set; }
public bool IsFragile { get; set; }
public CustomerTier Tier { get; set; }
}
但實際算金額的邏輯,被寫在另一個類別裡:
public class CartPricingHelper
{
public decimal CalculateSubtotal(Cart cart)
{
decimal subtotal = cart.Qty * cart.UnitPrice;
// CartPricingHelper 知道「VIP 買滿 5 件打 9 折」這條 Cart 內部的定價規則
if (cart.Tier == CustomerTier.Vip && cart.Qty >= 5)
{
subtotal *= 0.9m;
}
// CartPricingHelper 還知道「超商取貨的易碎品要加收處理費」這條規則
if (cart.DeliveryMethod == "超商" && cart.IsFragile)
{
subtotal += 30;
}
return subtotal;
}
}
單看 CartPricingHelper,會發現它知道的事情,遠超過「算一個總金額」該知道的範圍:
這些數字跟規則,理論上該是 Cart 自己的秘密CartPricingHelper 卻對它瞭若指掌,甚至比 Cart 自己還清楚
這帶來一個很實際的風險:Cart 的欄位或規則只要調整一點點,CartPricingHelper 就可能跟著壞掉,而且不會有任何警訊提醒你「這兩個類別,其實綁在一起」
這就是不適當的親密關係最危險的地方
兩個類別看起來各自獨立,實際上動一個,就會牽動另一個
而且這條牽連的線,沒有寫在任何介面上,只存在於某個工程師的腦子裡
解法同樣是搬移方法 (Move Method):把定價規則,搬回 Cart 自己的地盤
public class Cart
{
public int Qty { get; set; }
public decimal UnitPrice { get; set; }
public string DeliveryMethod { get; set; }
public bool IsFragile { get; set; }
public CustomerTier Tier { get; set; }
public decimal CalculateSubtotal()
{
decimal subtotal = Qty * UnitPrice;
if (Tier == CustomerTier.Vip && Qty >= 5)
{
subtotal *= 0.9m;
}
if (DeliveryMethod == "超商" && IsFragile)
{
subtotal += 30;
}
return subtotal;
}
}
CartPricingHelper 不再需要存在,呼叫端直接請 Cart 自己算:
var subtotal = cart.CalculateSubtotal();
現在只有 Cart 自己知道折扣門檻是多少、加收費用是多少
外部世界只需要知道「總金額算得出來」,不需要知道規則的每一個細節
依戀情結通常是單向的「一個方法,伸手向另一個類別要資料」
不適當的親密關係,經常是雙向的
A存取B的內部細節,B也反過來存取A的內部細節,兩者互相依賴對方的實作方式
這種雙向糾纏,是最危險的耦合形式
修改任何一邊,都可能牽動另一邊,卻沒有一個清楚的方向可以判斷「該從哪裡開始拆」
遇到這種情況,第一步通常不是急著搬移邏輯,而是先 「把雙向關聯改為單向」
決定哪一邊該是「知情的一方」,哪一邊該是「只透過公開介面詢問的一方」
先把關係理順,才有辦法談後續的重構
「子類別跟父類別之間的親密,是繼承關係本身就允許的」
子類別本來就該了解父類別暴露給它的保護成員
如果是靠委派 (Delegation) 硬是模擬出類似繼承的親密關係,那通常代表
直接改用繼承,會讓意圖更清楚
明天我們看一種更迂迴的耦合:一句簡單的請求,要接連問過好幾個物件,才能問到真正的答案
模組五第三站:訊息鏈(Message Chains)