You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中获取派生类属性的最优代码设计方案咨询

符合SOLID原则的优化方案

针对你的问题,核心是现有方案违反了接口隔离原则(ISP)——不该让Armor依赖它不需要的GetDamage方法,而dynamic_cast则会引入硬编码的类型判断,违反开闭原则(OCP)。下面是两种更优的设计思路:

方案一:拆分功能接口

把不同物品的能力拆成独立的纯虚接口,让需要对应能力的派生类去实现,避免基类臃肿,严格遵循接口隔离原则。

#include <iostream>
#include <map>
#include <string>

// 基础物品接口,仅包含所有物品共有的行为/属性
class Item {
public:
    virtual ~Item() = default; // 基类必须声明虚析构,防止内存泄漏
    // 可添加所有物品通用方法,比如GetName()、GetWeight()等
};

// 伤害提供者接口,仅由需要输出伤害的物品实现
class IDamageProvider {
public:
    virtual ~IDamageProvider() = default;
    virtual int GetDamage() const = 0;
};

// 防御提供者接口,同理
class IDefenseProvider {
public:
    virtual ~IDefenseProvider() = default;
    virtual int GetDefense() const = 0;
};

class Weapon : public Item, public IDamageProvider {
public:
    Weapon(int InDamage) : Damage(InDamage) {}

    int GetDamage() const override { return Damage; }

private:
    int Damage = 0;
};

class Armor : public Item, public IDefenseProvider {
public:
    Armor(int InDefence) : Defence(InDefence) {}

    int GetDefense() const override { return Defence; }

private:
    int Defence = 0;
};

class ItemManager {
public:
    ItemManager() {
        ItemMap.insert({
            {"Weapon", new Weapon(50)},
            {"Armor", new Armor(100)},
        });
    }

    ~ItemManager() {
        // 主动清理内存,避免泄漏
        for (auto& pair : ItemMap) {
            delete pair.second;
        }
    }

    Item* GetItem(const std::string& ItemName) const {
        auto It = ItemMap.find(ItemName);
        return It != ItemMap.end() ? It->second : nullptr;
    }

private:
    std::map<std::string, Item*> ItemMap;
};

int main() {
    ItemManager Manager;
    Item* ItemInst = Manager.GetItem("Weapon");

    // 通过接口查询能力,而非直接转换到具体类型
    if (auto* DamageProvider = dynamic_cast<IDamageProvider*>(ItemInst)) {
        std::cout << DamageProvider->GetDamage() << std::endl;
    }

    return 0;
}

方案优势:

  • 严格遵循接口隔离原则:Armor无需实现它用不到的伤害接口,每个接口只负责单一功能。
  • 符合开闭原则:后续新增物品类型(比如饰品Trinket)时,只需实现对应的接口(比如IManaProvider),无需修改现有基类或代码。
  • 类型转换被限制在功能接口层面,比转换到具体类的耦合度更低、灵活性更强。

方案二:访问者模式(Visitor Pattern)

如果后续需要对物品执行多种不同操作(比如计算伤害、计算防御、生成描述文本),访问者模式会更合适,它把操作逻辑和物品类型彻底解耦。

#include <iostream>
#include <map>
#include <string>

// 前置声明具体物品类
class Weapon;
class Armor;

// 访问者接口,定义所有支持的物品操作
class ItemVisitor {
public:
    virtual ~ItemVisitor() = default;
    virtual void Visit(Weapon& Weapon) = 0;
    virtual void Visit(Armor& Armor) = 0;
};

// 基础物品类,提供接受访问者的接口
class Item {
public:
    virtual ~Item() = default;
    virtual void Accept(ItemVisitor& Visitor) = 0;
};

class Weapon : public Item {
public:
    Weapon(int InDamage) : Damage(InDamage) {}

    int GetDamage() const { return Damage; }

    void Accept(ItemVisitor& Visitor) override {
        Visitor.Visit(*this);
    }

private:
    int Damage = 0;
};

class Armor : public Item {
public:
    Armor(int InDefence) : Defence(InDefence) {}

    int GetDefense() const { return Defence; }

    void Accept(ItemVisitor& Visitor) override {
        Visitor.Visit(*this);
    }

private:
    int Defence = 0;
};

// 具体访问者:获取物品伤害
class DamageGetter : public ItemVisitor {
public:
    void Visit(Weapon& Weapon) override {
        CurrentDamage = Weapon.GetDamage();
    }

    void Visit(Armor& Armor) override {
        CurrentDamage = 0; // 护甲无伤害输出
    }

    int GetResult() const { return CurrentDamage; }

private:
    int CurrentDamage = 0;
};

class ItemManager {
public:
    ItemManager() {
        ItemMap.insert({
            {"Weapon", new Weapon(50)},
            {"Armor", new Armor(100)},
        });
    }

    ~ItemManager() {
        for (auto& pair : ItemMap) {
            delete pair.second;
        }
    }

    Item* GetItem(const std::string& ItemName) const {
        auto It = ItemMap.find(ItemName);
        return It != ItemMap.end() ? It->second : nullptr;
    }

private:
    std::map<std::string, Item*> ItemMap;
};

int main() {
    ItemManager Manager;
    Item* ItemInst = Manager.GetItem("Weapon");

    DamageGetter Getter;
    if (ItemInst) {
        ItemInst->Accept(Getter);
        std::cout << Getter.GetResult() << std::endl;
    }

    return 0;
}

方案优势:

  • 完全消除了类型转换,遵循依赖倒置原则:操作逻辑(访问者)依赖抽象的ItemVisitor接口,而非具体物品类型。
  • 完美符合开闭原则:新增操作时只需添加新的访问者类,无需修改任何物品类代码。
  • 适合复杂业务场景:当需要对物品执行多种不同逻辑时,能避免在物品类中堆砌大量无关方法,保持代码结构清晰。

内容的提问来源于stack exchange,提问作者RobsBiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:54:51