C++通过构造函数注入"策略"

C++ injecting 'strategies' through constructor

本文关键字:策略 注入 构造函数 C++      更新时间:2023-10-16

我正在开发一些复杂的计算,需要确定"步骤"(策略模式类型实现),我不确定将步骤子计算类注入主类的最佳方法。

我研究过基于策略的设计,但读到策略设计是针对"编译时多态性"而不是运行时的。另外,我不确定如何使用模板,因为一些子计算类需要构造函数参数。

我已经开始为每个步骤实现虚拟的"接口"类,并在构造函数中注入每个步骤作为unique_ptr,但不确定这是否正确的"现代c++"方式。

我最初开始在主类中实现所有功能,但发现这使得独立地对每个步骤进行单元测试变得困难(如果不是不可能的话)。

结构类似如下:

class CalculationStepA
{
public:
  // default constructor
  StepAResult performStep(const input& requiredInput);
};
class CalculationStepBType1
{
public:
  // default constructor
  StepBResult performStepB(const stepBInput& requiredInput);
};
class CalculationStepBType2
{
public:
  CalculationStepBType2(const inputIOnlyNeedForType2& parameters)
  {
    // initialize class members from input
    // need for this calculation type
  }
  StepBResult performStepB(const stepBInput& requiredInput);
};
class CalculationStepCType1
{
public:
  CalculationStepBType2(const inputIOnlyNeedForType1& parameters)
  {
    // initialize class members from input
    // need for this calculation type
  }
  StepCResult performStepC(const stepCInput& requiredInput);
};
class CalculationStepCType2
{
public:
  CalculationStepBType2(const inputIOnlyNeedForType2& parameters)
  {
    // initialize class members from input
    // need for this calculation type
  }
  StepCResult performStepB(const stepCInput& requiredInput);
};
class ClassThatUsesAllTheCalculations
{
public:
  ClassThatUsesAllTheCalculations(/* take required parameters that determine which step types I need */)
  {}
  // possible constructor?
  ClassThatUsesAllTheCalculations(
       std::unique_ptr<IStepACalculationStrategy> stepA, 
       std::unique_ptr<IStepBCalculationStrategy> stepB,    
      std::unique_ptr<IStepCCalculationStrategy> stepC)
  {
  }
  FinalResult executeCalculation(const finalInputRequiredHere& input)
  {
    auto stepAresult = stepACalculator(somethingFromInput);
    // logic to use stepA and determine if we should continue
    auto stepBresult = stepBCalculator(somethingFromStepAResult);
    // again, logic to use stepB and determine if we should continue
    auto stepCresult = stepCCalculator(somethingFromStepBResult);
    // assemble final result
    return theFinalResult
  }

  // other method needed to setup calculation

private:
  TypeForStepACalculation stepACalculator;
  TypeForStepBCalculation stepBCalculator;
  TypeForStepCCalculation stepCCalculator;
};

简单继承怎么样?

struct StepA{
    virtual StepAResult perform(StepAParams)=0;
};
struct someStepAImpl : public StepA{
    virtual StepAResult perform(StepAParams params) override {
        //actual implementation
    }
};

计算类是否使用引用(必须在构造时设置),std::reference_wrapper(不是null,但可以稍后更改)或某种(智能)指针(可能是nullptr,不要忘记检查,但在管理生命周期时最容易)取决于您计划如何使用它以及您希望如何管理对象的生命周期。使用unique_ptr的一个例子是:

class Calculator
{
public:
    Calculator(
        std::unique_ptr<StepA> stepA, 
        std::unique_ptr<StepB> stepB,    
        std::unique_ptr<StepC> stepC
    )
    :m_stepA(std::move(stepA)),m_stepB(std::move(stepB)),m_stepC(std::move(stepC))
    {}
    FinalResult executeCalculation(const finalInputRequiredHere& input)
    {
        //logic
        auto stepAresult = stepA->perform(StepAParams);
        //logic
        auto stepBresult = stepB->perform(StepBParams);
        //logic
        auto stepCresult = stepC->perform(StepAParams);
        //logic
        return FinalResult();
    }
private:
    std::unique_ptr<StepA> m_stepA=nullptr;
    std::unique_ptr<StepB> m_stepB=nullptr;
    std::unique_ptr<StepC> m_stepC=nullptr;
};

void somewhereElse(){
    std::unique_ptr<StepA> stepa(new someStepAImpl());
    std::unique_ptr<StepB> stepa(new someStepBImpl());
    std::unique_ptr<StepC> stepa(new someStepCImpl());
    Calculator calc(
        std::move(stepa),
        std::move(stepb),
        std::move(stepc)
    );
    calc.executeCalculation(...);
}