盡量使用 **組合/聚合**(Has-a)關系復用代碼,而非**繼承**(Is-a)。新對象持有舊對象引用,通過委派調用舊對象方法實現功能擴展。
| 繼承復用 | 組合復用 |
|---|---|
| 靜態、編譯期確定 | 動態、運行期可替換 |
| 破壞封裝(父類細節暴露給子類) | 保持封裝(只依賴接口) |
| 父類變更強制子類變更 | 低耦合,易維護 |
| 「白箱」復用 | 「黑箱」復用 |
// 策略接口
interface PayStrategy { void pay(double amount); }
// 具體策略
class Alipay implements PayStrategy { public void pay(double a) { ... } }
class WechatPay implements PayStrategy { public void pay(double a) { ... } }
// 上下文(組合策略)
class Order {
private PayStrategy strategy; // 組合
public void setStrategy(PayStrategy s) { this.strategy = s; }
public void checkout(double amt) { strategy.pay(amt); }
}
// 使用
Order order = new Order();
order.setStrategy(new Alipay()); // 運行期切換
order.checkout(99.9);
`Order` **不繼承** `PayStrategy`,而是**組合**它,運行時動態切換支付方式,完美體現合成復用。
「有沒有」比「是不是」更靈活。設計時首問:「能不能用組合?」能則組合,不能再考慮繼承。