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
相关产品推荐
相关产品推荐

