为类扩展能力的设计模式:解决Database成员调用歧义问题
解决类继承中Database成员歧义的设计方案
当前代码的核心问题是每个类(Person类和能力类)都独立持有Database实例,这不仅导致this->database的访问歧义(编译器无法区分派生类与基类的同名成员),还造成了资源冗余,甚至可能出现不同实例状态不一致的问题。以下是两种可行的解决方案:
方案一:重构能力类,通过参数传递Database
直接修改能力类,让它们不再持有Database,而是在调用方法时传入Database引用,从根源消除歧义:
// 能力类不再持有Database,改为方法参数接收 class CanTalkEnglish{ public: void TalkEnglish(const Database& database){ Console.Write(" am taking {database.MyLanguage}"); } }; class CanTalkFrench{ public: void TalkFrench(const Database& database){ Console.Write("Je parle {database.MyLanguage}"); } }; // Person类仅保留一个Database实例,调用能力方法时传入 class Englishman : public CanTalkEnglish { private: Database database; public: void Talk(){ if (this->database.Mood == "OpenToCommunication"){ this->TalkEnglish(this->database); } } }; class CanadaMan : public CanTalkEnglish, CanTalkFrench { private: Database database; public: void Talk(){ if (this->database.Mood == "OpenToCommunication"){ this->TalkEnglish(this->database); this->TalkFrench(this->database); } } };
这种方式改动最小,立即解决歧义问题,同时保证每个Person只有一个Database实例。
方案二:使用策略模式,实现行为与主体解耦
如果需要更灵活的扩展(比如新增说话语言、新增Person类型),策略模式是更优选择。将"说话"行为封装为独立策略,Person类通过组合策略来实现功能,彻底避免继承带来的成员歧义:
// 定义说话策略的抽象接口 class TalkStrategy { public: virtual void Talk(const Database& database) = 0; virtual ~TalkStrategy() = default; // 虚析构保证正确销毁 }; // 具体的英语说话策略 class EnglishTalk : public TalkStrategy { public: void Talk(const Database& database) override { Console.Write(" am taking {database.MyLanguage}"); } }; // 具体的法语说话策略 class FrenchTalk : public TalkStrategy { public: void Talk(const Database& database) override { Console.Write("Je parle {database.MyLanguage}"); } }; // 抽象Person类,持有Database和策略集合 class Person { protected: Database database; std::vector<std::unique_ptr<TalkStrategy>> talkStrategies; public: void Talk(){ if (database.Mood == "OpenToCommunication"){ for (auto& strategy : talkStrategies){ strategy->Talk(database); } } } }; // 英国人:仅添加英语说话策略 class Englishman : public Person { public: Englishman(){ talkStrategies.emplace_back(std::make_unique<EnglishTalk>()); } }; // 加拿大人:添加英语和法语说话策略 class CanadaMan : public Person { public: CanadaMan(){ talkStrategies.emplace_back(std::make_unique<EnglishTalk>()); talkStrategies.emplace_back(std::make_unique<FrenchTalk>()); } };
策略模式的优势:
- 彻底消除继承带来的成员歧义问题;
- 符合开闭原则:新增语言或Person类型时,只需新增策略类,无需修改现有代码;
- 行为组合更灵活:可以动态添加/移除说话策略(比如加拿大人根据场景切换语言)。
核心思路总结
无论选择哪种方案,核心都是让Database实例唯一化,避免多个类重复持有相同的资源,同时通过依赖传递(参数或组合)让需要使用Database的类获取到正确的实例,从根源解决访问歧义。
内容的提问来源于stack exchange,提问作者fobus
相关产品推荐
相关产品推荐

