C++中两类Evaluator共用辅助方法的面向对象设计问题
现有如下C++类结构:
struct Arg { Info someSpecificInfo; OtherInfo anotherInfo; }; class BaseEvaluator { public: BaseEvaluator(Info info) {}; virtual Result evaluate(); }; class SpecificEvaluator1 : public BaseEvaluator { public: SpecificEvaluator1(Info info): BaseEvaluator(info) {}; Result evaluate(); }; class SpecificEvaluator2: public BaseEvaluator { public: SpecificEvaluator2(Info info): BaseEvaluator(info) {}; Result evaluate(); };
需要新增两个Evaluator类:
SpecificEvaluator3:仅依赖Info对象ArgSpecificEvaluator:依赖完整的Arg对象
按OOP原则,计划把原BaseEvaluator改为InfoEvaluator,新增ArgEvaluator类,二者共同继承一个只包含纯虚evaluate()方法的顶层BaseEvaluator基类。
但遇到的问题是:这两个新类有大量重复的辅助方法(helperMethod1、helperMethod2)——ArgSpecificEvaluator只要传入arg.someSpecificInfo作为参数,辅助方法的逻辑就和SpecificEvaluator3完全一致。
不想让所有Evaluator都依赖Arg(部分类不需要访问OtherInfo),最初尝试让辅助方法依赖成员变量Info但不好协调,所以改成了把Info作为参数传入辅助方法。现在想实现类似Java里「类继承基类同时实现带辅助方法的接口」的效果,问在C++里该怎么弄,要不要用多继承?
方案1:用纯虚类模拟接口 + 多继承
C没有Java那种专门的接口,但可以用纯虚类来模拟。把复用的辅助方法放到一个单独的类里(可以提供默认实现),让需要这些方法的Evaluator同时继承核心基类和这个辅助类就行——这就是C里对应Java「继承+实现接口」的常用方式,本质是多继承,但只要辅助类是单纯的逻辑混入类,不会有菱形继承的问题。
示例代码:
// 顶层抽象基类,只定义核心的evaluate接口 class BaseEvaluator { public: virtual ~BaseEvaluator() = default; // 基类必须加虚析构,避免内存泄漏 virtual Result evaluate() = 0; }; // 辅助方法类,封装复用的逻辑,用protected确保只有子类能访问 class InfoHelper { protected: void helperMethod1(const Info& info) { // 这里写复用的逻辑 } int helperMethod2(const Info& info) { // 这里写复用的逻辑 return 0; } }; // 仅依赖Info的基类,继承BaseEvaluator和InfoHelper class InfoEvaluator : public BaseEvaluator, protected InfoHelper { protected: Info m_info; public: InfoEvaluator(Info info) : m_info(std::move(info)) {} }; // SpecificEvaluator3直接继承InfoEvaluator,就能用辅助方法 class SpecificEvaluator3 : public InfoEvaluator { public: SpecificEvaluator3(Info info) : InfoEvaluator(std::move(info)) {} Result evaluate() override { helperMethod1(m_info); int val = helperMethod2(m_info); // 这里写SpecificEvaluator3的专属逻辑 return Result{}; } }; // 依赖Arg的基类,同样继承BaseEvaluator和InfoHelper class ArgEvaluator : public BaseEvaluator, protected InfoHelper { protected: Arg m_arg; public: ArgEvaluator(Arg arg) : m_arg(std::move(arg)) {} }; // ArgSpecificEvaluator继承ArgEvaluator,用arg里的Info调用辅助方法 class ArgSpecificEvaluator : public ArgEvaluator { public: ArgSpecificEvaluator(Arg arg) : ArgEvaluator(std::move(arg)) {} Result evaluate() override { helperMethod1(m_arg.someSpecificInfo); int val = helperMethod2(m_arg.someSpecificInfo); // 这里写依赖OtherInfo的专属逻辑 return Result{}; } };
方案2:组合而非继承(更推荐)
如果不想用多继承,也可以用「组合优于继承」的思路,把InfoHelper作为成员变量放到需要的Evaluator类里:
class InfoHelper { public: void helperMethod1(const Info& info) { // 复用逻辑 } int helperMethod2(const Info& info) { // 复用逻辑 return 0; } }; class InfoEvaluator : public BaseEvaluator { protected: Info m_info; InfoHelper m_helper; // 组合辅助类 public: InfoEvaluator(Info info) : m_info(std::move(info)) {} }; class SpecificEvaluator3 : public InfoEvaluator { public: SpecificEvaluator3(Info info) : InfoEvaluator(std::move(info)) {} Result evaluate() override { m_helper.helperMethod1(m_info); int val = m_helper.helperMethod2(m_info); // 专属逻辑 return Result{}; } }; class ArgEvaluator : public BaseEvaluator { protected: Arg m_arg; InfoHelper m_helper; public: ArgEvaluator(Arg arg) : m_arg(std::move(arg)) {} }; class ArgSpecificEvaluator : public ArgEvaluator { public: ArgSpecificEvaluator(Arg arg) : ArgEvaluator(std::move(arg)) {} Result evaluate() override { m_helper.helperMethod1(m_arg.someSpecificInfo); int val = m_helper.helperMethod2(m_arg.someSpecificInfo); // 专属逻辑 return Result{}; } };
这种方式避免了多继承的潜在问题,代码也更灵活,比如后续可以给不同的Evaluator传入不同的InfoHelper实例(如果需要的话)。
方案3:模板基类(适合扩展更多数据源)
如果后续可能新增更多类型的数据源,还可以用模板基类来统一处理:
template<typename DataSource> class EvaluatorWithHelper : public BaseEvaluator, protected InfoHelper { protected: DataSource m_data; public: EvaluatorWithHelper(DataSource data) : m_data(std::move(data)) {} }; // 针对Info的Evaluator class SpecificEvaluator3 : public EvaluatorWithHelper<Info> { public: SpecificEvaluator3(Info info) : EvaluatorWithHelper(std::move(info)) {} Result evaluate() override { helperMethod1(m_data); // 专属逻辑 return Result{}; } }; // 针对Arg的Evaluator class ArgSpecificEvaluator : public EvaluatorWithHelper<Arg> { public: ArgSpecificEvaluator(Arg arg) : EvaluatorWithHelper(std::move(arg)) {} Result evaluate() override { helperMethod1(m_data.someSpecificInfo); // 专属逻辑 return Result{}; } };
这种方式能进一步减少重复代码,适合场景比较复杂的情况。
内容的提问来源于stack exchange,提问作者kohrhea

