假設我們要模擬各種鴨子:綠頭鴨、橡皮鴨、誘餌鴨。初期用繼承:
abstract class Duck { void quack(); void fly(); }
class MallardDuck extends Duck { ... }
class RubberDuck extends Duck {
// 問題:橡皮鴨不會飛、叫聲是吱吱
@Override void fly() { /* 什麼都不做 */ }
@Override void quack() { System.out.println("吱吱"); }
}
缺點顯而易見:
fly/quack。「找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混合在一起。」
將 fly 和 quack 從 Duck 中提取,定義為介面:
interface FlyBehavior { void fly(); }
interface QuackBehavior { void quack(); }
// 飛行策略
class FlyWithWings implements FlyBehavior {
public void fly() { System.out.println("我會飛"); }
}
class FlyNoWay implements FlyBehavior {
public void fly() { System.out.println("我不會飛"); }
}
// 叫聲策略
class Quack implements QuackBehavior {
public void quack() { System.out.println("呱呱"); }
}
class Squeak implements QuackBehavior {
public void quack() { System.out.println("吱吱"); }
}
class MuteQuack implements QuackBehavior {
public void quack() { System.out.println("沉默"); }
}
abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void performFly() { flyBehavior.fly(); }
public void performQuack() { quackBehavior.quack(); }
// 關鍵:提供設置器,支援運行時動態變更行為
public void setFlyBehavior(FlyBehavior fb) { flyBehavior = fb; }
public void setQuackBehavior(QuackBehavior qb) { quackBehavior = qb; }
abstract void display(); // 外觀差異由子類實現
}
class MallardDuck extends Duck {
public MallardDuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
public void display() { System.out.println("我是綠頭鴨"); }
}
class DecoyDuck extends Duck {
public DecoyDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new MuteQuack();
}
public void display() { System.out.println("我是誘餌鴨"); }
}
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performQuack(); // 輸出: 呱呱
// 運行時動態改變:綠頭鴨突然失聲了
mallard.setQuackBehavior(new MuteQuack());
mallard.performQuack(); // 輸出: 沉默
}
}
Duck 依賴 FlyBehavior 介面,而非具體類。Quack),可動態替換。Duck)。定義了演算法族,分別封裝起來,讓它們之間可以相互替換。此模式讓演算法的變化獨立於使用演算法的客戶。
適用場景:同一行為有多種實現 (如支付方式、排序算法、壓縮策略、路由規則),且需在運行時切換。