非虚接口中带链式方法的Getter实现方案问询
实现方案
核心思路
不需要额外存储全局/外部map,也无需修改工厂函数,核心是通过在基类Foo中新增非虚接口Get,返回一个类型转换代理类的方式实现链式调用。代理类持有从map中取出的原始字符串值,提供AsString()、AsInt()等类型转换方法,既保留NVI模式的封装性,又满足链式调用需求。
具体实现步骤
1. 定义类型转换代理类
这个类负责存储原始字符串,并提供各种类型转换逻辑,可按需扩展转换方法:
class ValueProxy { private: std::string m_rawValue; public: explicit ValueProxy(std::string val) : m_rawValue(std::move(val)) {} // 返回原始字符串 std::string AsString() const { return m_rawValue; } // 转换为int,可根据业务需求添加错误处理(如格式校验、默认值) int AsInt() const { return std::stoi(m_rawValue); } // 扩展其他类型转换,比如double、bool double AsDouble() const { return std::stod(m_rawValue); } };
2. 扩展基类Foo的NVI接口
在基类中新增公共非虚方法Get,调用私有虚函数获取对应字符串值,再包装成代理类返回:
class Foo { public: // 原有NVI接口 void save_to_map() { getRecord(); } // 新增链式调用的NVI接口 ValueProxy Get(unsigned int key1, const std::string& key2) { return ValueProxy(getTargetValue(key1, key2)); } private: // 原有私有虚函数:负责填充子类内部的map virtual void getRecord() = 0; // 新增私有虚函数:子类实现双key的查找逻辑 virtual std::string getTargetValue(unsigned int key1, const std::string& key2) = 0; };
3. 修改子类Bar实现虚函数
子类Bar复用原有的map存储SQL查询结果,新增getTargetValue的实现来完成双key的查找:
class Bar : public Foo { private: std::map<std::pair<unsigned int, std::string>, std::string> m_dataMap; void getRecord() override { // 原逻辑:通过SQLite的tableSelect填充m_dataMap // 示例: // m_dataMap[{0, "A"}] = "test_string"; // m_dataMap[{0, "B"}] = "456"; } std::string getTargetValue(unsigned int key1, const std::string& key2) override { auto iter = m_dataMap.find({key1, key2}); if (iter != m_dataMap.end()) { return iter->second; } // 可根据需求返回默认值或抛出异常 return ""; } };
4. 调用方式
工厂函数完全不需要修改,创建对象后直接使用链式接口:
std::unique_ptr<Foo> p = createFoo(); // 原工厂函数逻辑不变 p->save_to_map(); // 先执行数据填充 std::string strResult = p->Get(0, "A").AsString(); int intResult = p->Get(0, "B").AsInt();
方案优势
- 严格遵循NVI模式:基类控制对外接口,子类仅实现内部逻辑,封装性不受破坏
- 无额外存储成本:复用子类原有的map存储查询结果,无需新增全局或外部容器
- 扩展性强:新增类型转换只需在
ValueProxy中添加对应方法,无需修改核心业务逻辑 - 语义清晰:链式调用的写法直观,符合业务代码的阅读习惯
内容的提问来源于stack exchange,提问作者Namal
相关产品推荐
相关产品推荐

