如何将策略模式与 STL 容器一起使用

How to use the strategy pattern with STL containers?

本文关键字:一起 STL 策略 模式      更新时间:2023-10-16

>假设我有一个名为 BinaryClassifier 的策略接口,它可以接受一个Sample并返回一个double,表示Sample对象属于正类的概率:

struct BinaryClassifier {
  virtual ~BinaryClassifier(){}
  virtual double classify(std::shared_ptr<Sample> sample) const = 0;
};

我们可能有几种BinaryClassifier的实现,例如,LogisticRegressionBinaryClassifier

Sample又是一个仅公开两种方法的接口:

struct Sample {
  virtual ~Sample() {}
  InputFeatures const& get_input_features() const = 0;
  double get_label() const = 0;
};

除了这两种方法之外,Sample的具体实现公开了完全不同的接口(即,它们是不相关的(,它们唯一的共同点是它们可以通过二进制分类器进行分类。

目前为止,一切都好。

当我们决定引入BinaryClassifier::train方法时,就会出现问题:

struct BinaryClassifier {
  virtual ~BinaryClassifier(){}
  virtual double classify(std::shared_ptr<Sample> sample) const = 0;
  virtual void train(std::vector<std::shared_ptr<Sample>> samples) = 0;
};

此时,以下操作将不起作用:

std::vector<std::shared_ptr<ConcreteSample>> concreteSamples = ...;
concreteBinaryClassifier.train(concreteSamples);

这是因为std::vector<std::shared_ptr<ConcreteSample>>std::vector<std::shared_ptr<Sample>>是两种不相关的类型。

C++的解决方案是依赖模板:

      template<class SampleType>
      virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0; // non-working code, template method cannot be virtual

但是模板方法不能virtual。尽管如此,我希望BinaryClassifier成为一个策略界面,因为可能存在许多可能的BinaryClassifier实现。在这一点上,尽管设计看起来很合理,但我陷入了死胡同。

编辑:此外,在对类型ConcreteSampleB的对象进行分类时,可能会发生给定的BinaryClassifier对象使用ConcreteSampleA向量进行训练的情况

哪种方式是以

最C++的方式对这种情况进行建模的正确方法?

你可以把你的 BinaryClassifier 变成一个模板类

template<SampleType> class BinaryClassifier
{
    virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0;
}

您不能在ConcreteSampleA上训练BinaryClassifier,然后使用它对任何随机ConcreteSampleB进行分类。因此,样本类型是BinaryClassifier固有的一部分。Nullref 的答案在那里是合理的:将示例类型设置为模板参数。

正如您所发现的,这意味着不再需要Sample接口。好。 std::vector<int>也不要求int派生自某些Element接口。

当你摆脱东西时,InputFeatures看起来也很可疑。我只想说get_input_features必须返回一些std::tuple其成员类型都std::less定义。由于它不再是虚拟的,因此您不在乎不同的样本类型返回不同的元组。而且我绝对不会硬编码get_label必须返回double.无论如何,这是一种奇怪的标签类型。

现在您说使用样本类型 A 进行训练,然后对样本类型 B 进行分类可能是有意义的。这就是你得到改进的地方:似乎实际的兼容性要求是它们返回相同的元组。因此,Nullref 更好的解决方案是在 get_input_sample 返回的元组类型上模板化BinaryClassifier

[编辑]此外,classify不需要共同所有权。传递Sample const& . train()真的应该只采用一个迭代器对。C++约定是将一组对象作为范围传递。