在面向 object 的继承中使用协方差避免冗余代码

Avoid redundancy code using covariance in object oriented inheritance

我知道以前可能有人问过这个问题,但我真的不知道要搜索什么,因为我什至不确定我选择的标题。

我想达到的目标: 如果我将 getMeasurement(int timestep) 与传感器映射 object 中不包含的整数值一起使用,则应使用特定的测量值对要求的测量值进行插值(如果可能)方法插值(...)。 它应该从地图中获得正确的两个测量值 objects 以用于超级 class 传感器中的插值并对它们进行插值,但我不知道如何以及是否可以调用插值。也许使用 Generics/Typename 或设计模式。

Sensor someSensor = ...
Measurement measurementAt2 = someSensor.getMeasurement(2); 
// should interpolate value if map measurements in someSensor not has the key 2

附加信息: ASensor::measurements 仅包含 AMeasurementsBSensor::measurements 只包含 BMeasurements。 ...这些测量包含不同的值类型,因此测量的每个子 class 需要以不同方式进行插值。

abstract class Sensor {
   map<int, Measurement> measurements;
   Measurement getMeasurement(int timestep);
}

class ASensor : Sensor {
   ...
}

class BSensor : Sensor {
   ...
}


abstract class Measurement {
   ...
}

class AMeasurement : Measurement {
   AMeasurement interpolate(AMeasurement other, int timestep);
}

class BMeasurement : Measurement {
   BMeasurement interpolate(BMeasurement other, int timestep);
}

如果我在 Measurement for inheritance 中添加一个 abstract/virtual 方法 Measurement interpolated(Measurement other, int timestep),这个签名对 subclass,因为我需要检查 class 类型并转换其他测量值。

我很欣赏用我目前正在使用的 C++11 编写代码的答案。

编辑:如果需要此信息,子classes ASensor+AMeasurement、BSensor+BMeasurement...都是独立加载的插件。

EDIT2:添加了 return 类型的方法(我忘记了)。

我不完全确定你的问题,但在 c++ 中可以获得 return 值协方差。也许这样的事情会对你有所帮助(见评论):

#include <iostream>
using namespace std;

struct Measurement //Abstract
{
    virtual int apply(int timestep) const = 0;
    virtual std::string name() const = 0;
  protected:
    virtual ~Measurement(){}
  //...virtual Meas... etc
};

struct AMeasurement : Measurement //Implements Measurement for sensorA
{
  std::string name() const override{ return "AMeasurement"; }
  int apply(int timestep) const override
  {
    return timestep * 10;
  }
};
struct BMeasurement : Measurement //Implements Measurement for sensorB
{
  std::string name() const override{ return "BMeasurement"; }
  int apply(int timestep) const override
  {
    return timestep * 20;
  }
};

struct MeasurementProvider //Provides measurement
{
  virtual const Measurement& getMeasurement() const = 0;
  //...etc
  protected:
    virtual ~MeasurementProvider(){}
};

//Generalized measurement provider.
// Covariance ensure correct measurement used. Currently most basic
// implementation. Can elaborate
template <class MeasurementType>
struct GenMeasurementProvider : MeasurementProvider
{
  //NOTE: MeasureType derived from Measurement, hence covariance...
  const MeasurementType& getMeasurement() const override{return m_;}
  MeasurementType m_;
};

// Perhaps Sensor is just a generalized Provider.
struct SensorA : GenMeasurementProvider<AMeasurement>
{
};

// Interpolate using provider instead of actual measurement to
// allow for covariance.
void interpolate(const MeasurementProvider& provider, int timestep)
{
  //return type allows covariance, therefore apply to be
  // called on correct type
  auto const& measurement = provider.getMeasurement();

  std::cout << "Result of measurement " << measurement.name() 
            << ":" << measurement.apply(timestep) << std::endl;
}

int main() {
  const int timestep = 100;
  interpolate(GenMeasurementProvider<AMeasurement>{}, timestep);
  interpolate(GenMeasurementProvider<BMeasurement>{}, timestep);
  interpolate(SensorA{}, timestep);
  return 0;
}

我省略了很多细节,我可能会用颜色更清楚。

如果您的问题是 interpolate() 的签名,我想您可以根据派生的 class 在模板 class 中转换 Measurement;像

template <typename Derived>
class Measurement {
   Derived interpolate (Derived other, int timestep)
    { /* do something */ }
};

class AMeasurement : Measurement<AMeasurement> {
   // ...
};

class BMeasurement : Measurement<BMeasurement> {
   // ...
};

p.s.: 对不起我的英语不好

如果您在 Sensor 中创建纯虚拟的 getMeasurement 函数,那么您不需要 class 基类中的 map。然后由 Sensor 的实现来存储它们自己类型的测量值并在它们之间进行插值。您可以提供模板 class 来完成所有工作:

class Sensor {
   public:
     virtual std::unique_ptr<Measurement> getMeasurement(int timestep) const = 0;
};

template<typename M>
class BasicSensor : public Sensor {
    std::map<int, M> measurements;
  public:
    std::unique_ptr<Measurement> getMeasurement(int timestep) const override {
        auto itr = measurements.lower_bound(timestep);

        if (itr == measurements.end())  // Cant find measurement equal or later
            return nullptr;             // than timestep so can't interpolate.

        if (itr->first == timestep)                   // Found exact match so
            return std::make_unique<M>(itr->second);  // don't need to interpolate.

        if (itr == measurements.begin()) // Can't find measurement before
            return nullptr;              // timestep so can't interpolate.

        auto interpolated = std::prev(itr)->second.interpolate(itr->second, timestep);

        // Copy to smart-pointer to avoid slicing
        return std::make_unique<M>(interpolated); 
    }
    void setMeasurement(int timestep, const M& m) {
        measurements[timestep] = m;
    }
};

class ASensor : public BasicSensor<AMeasurement> {};
class BSensor : public BasicSensor<BMeasurement> {};

Live demo.