策略模式

它和模板方法模式是亲兄弟,解决的都是“算法封装”的问题,但手段截然不同:模板方法用的是继承(比较死板),策略模式用的是组合(非常灵活)。


1. 策略模式 (Strategy Pattern)

场景设定:僵尸游戏

假设你正在写一个打僵尸的游戏。玩家手中的武器是可以随时切换的:

  • 匕首时,近距离刺杀。
  • AK47时,远距离扫射。
  • 手雷时,范围爆炸。

如果你在 Player 类里写一堆 if (weapon == KNIFE) { ... } else if (weapon == GUN) { ... },代码会非常丑陋且难以维护。

代码示例

#include <iostream>
#include <memory> // 使用智能指针管理内存
using namespace std;

// ==========================================
// 1. 抽象策略 (Strategy Interface)
// 稳定点:无论什么武器,攻击这个动作的接口是统一的
// ==========================================
class IWeapon {
public:
    virtual ~IWeapon() {}
    // 纯虚函数:具体怎么攻击,由子类决定
    virtual void UseWeapon() = 0;
};

// ==========================================
// 2. 具体策略 (Concrete Strategies)
// 变化点:各种不同的攻击算法
// ==========================================

class Knife : public IWeapon {
public:
    void UseWeapon() override {
        cout << "  [攻击] 挥舞匕首 -> 造成 10 点近战伤害" << endl;
    }
};

class AK47 : public IWeapon {
public:
    void UseWeapon() override {
        cout << "  [攻击] AK47 突突突 -> 造成 50 点远程伤害" << endl;
    }
};

class Grenade : public IWeapon {
public:
    void UseWeapon() override {
        cout << "  [攻击] 扔出手雷 -> BOOM! 造成 100 点范围伤害" << endl;
    }
};

// ==========================================
// 3. 上下文环境 (Context)
// 稳定点:角色使用武器的流程
// ==========================================
class Character {
private:
    // 【核心】:使用组合 (Composition) 持有策略接口
    // 这里使用 unique_ptr 自动管理内存,也可以用原始指针
    IWeapon* weapon; 

public:
    Character() : weapon(nullptr) {}

    // 【动态切换策略】
    // 这是策略模式比模板方法强大的地方:运行时换脑子
    void SetWeapon(IWeapon* newWeapon) {
        this->weapon = newWeapon;
    }

    // 执行策略
    void Attack() {
        if (weapon == nullptr) {
            cout << "  [攻击] 赤手空拳 -> 造成 1 点伤害" << endl;
        } else {
            // 多态调用:委托给具体的策略对象
            weapon->UseWeapon();
        }
    }
};

// ==========================================
// 客户端代码
// ==========================================
int main() {
    Character player;
    
    // 1. 初始状态
    cout << "=== 遭遇普通僵尸 ===" << endl;
    Knife* knife = new Knife();
    player.SetWeapon(knife); // 装备匕首
    player.Attack();

    // 2. 动态切换算法
    cout << "\n=== 遭遇尸潮 ===" << endl;
    AK47* gun = new AK47();
    player.SetWeapon(gun); // 换枪
    player.Attack();

    // 3. 再次切换
    cout << "\n=== 遭遇 BOSS ===" << endl;
    Grenade* boom = new Grenade();
    player.SetWeapon(boom); // 换手雷
    player.Attack();

    // 清理内存
    delete knife;
    delete gun;
    delete boom;

    return 0;
}

1. 定义

  • 原文: 定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
  • 解读:
    • “一系列算法”:匕首、枪、手雷都是“攻击算法”。
    • “相互替换”:因为它们都实现了 IWeapon 接口,对于 Character 来说,它们是一样的,随时可以换。

2. 解决的问题 (Stable vs. Changing)

  • 稳定点 (Stable):客户的使用请求
    • Character 类中调用 Attack() 的时机和逻辑是稳定的。
  • 变化点 (Changing):具体的算法实现
    • 具体的伤害计算、攻击特效是变化的。

3. 策略模式 vs 模板方法模式 (核心对比)

这是面试必考题,两者都在做“算法抽象”,区别在哪里?

特性 模板方法 (Template Method) 策略模式 (Strategy Pattern)
复用方式 继承 (Class Inheritance) 组合 (Object Composition)
绑定时间 编译期 (Compile-time) 运行期 (Runtime)
灵活性 低(父类定死框架,无法中途改) (随时调用 SetWeapon 切换)
关系 "Is-A" (DolphinShow 是 ZooShow) "Has-A" (Character 有一把 Weapon)
适用场景 算法骨架固定,只有个别步骤变 整个算法逻辑经常整体替换

一句话总结: 如果你需要在程序运行过程中切换算法(比如打游戏换枪、商场促销切优惠券),必须用策略模式。

4. 符合哪些设计原则?

  • A. 开闭原则 (OCP)
    • 对扩展开放: 想加一个“激光枪”?新建 LaserGun 类继承 IWeapon 即可。
    • 对修改关闭: Character 类的代码完全不用动。
  • B. 合成复用原则 (Composition over Inheritance)
    • 这是策略模式的灵魂。它没有让 Character 去继承 Weapon,而是让 Character 持有 Weapon
    • 这避免了类爆炸(不需要 KnifeCharacter, GunCharacter 这种继承类)。

5. C++ 现代扩展 (Function 对象)

在现代 C++ (C++11 及以后) 中,如果策略很简单,我们甚至不需要写这么多类。可以使用 std::function 配合 Lambda 表达式来实现轻量级策略模式

#include <functional>

class Character {
    // 直接持有一个函数对象,而不是指针
    std::function<void()> attackStrategy; 
public:
    void SetStrategy(std::function<void()> strategy) {
        attackStrategy = strategy;
    }
    void Attack() {
        if(attackStrategy) attackStrategy();
    }
};

int main() {
    Character p;
    // 使用 Lambda 表达式直接定义策略,不需要写 Knife 类了
    p.SetStrategy([](){ cout << "挥刀!" << endl; }); 
    p.Attack();
    
    p.SetStrategy([](){ cout << "开枪!" << endl; });
    p.Attack();
}

总结

策略模式完美诠释了“把变化关进笼子”的思想:

  • 笼子: IWeapon 接口。
  • 猫: 各种具体的武器算法。
  • 主人: Character,手里拿着笼子的钥匙(指针),想装哪只猫就装哪只猫。
posted @ 2025-12-20 20:41  belief73  阅读(0)  评论(0)    收藏  举报