繁体   English   中英

基于策略的设计和最佳实践 - C ++

[英]Policy based design and best practices - C++

struct InkPen
{
  void Write()
  {
    this->WriteImplementation();
  }

  void WriteImplementation()
  {
    std::cout << "Writing using a inkpen" << std::endl;
  }

};

struct BoldPen
{
  void Write()
  {
    std::cout << "Writing using a boldpen" << std::endl;
  }
};

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

int main()
{
  Writer<InkPen> writer;
  writer.StartWriting();
  Writer<BoldPen> writer1;
  writer1.StartWriting();
  return 0;
}

我将上述代码编写为学习基于策略的设计的一部分 我对上面的代码几乎没有疑问

1 - 此实现看起来是否正确? 我的意思是:它真的看起来像一个基于政策的设计吗?

2 - 我现在可以将任何类型的笔钩到作家身上。 但是当我拿到没有默认构造函数的笔(仅参数化构造函数)时,我会怎么做? 我该如何处理这种情况?

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};

3 - 当使用上面的代码时

Writer<InkPen> writer;

我猜编译器会用InkPen替换PenType 如果是,为什么我不能从StartWriting()调用Write ()而不是前缀基类名( PenType :: Write() )?

4 - 我认为基于策略的设计会迫使您从语义无效的类派生。 在上面的代码中,只有因为作者使用笔才能从笔中导出作者。 但是说作家是笔在语义上是无效的。 有没有其他更好的方法来解决这个问题,或者我在这里遗漏了什么?

有什么想法吗?

以下是我将如何实现该类:

template<class PenType>
class Writer
{
public:
  Writer(const PenType& pen = PenType()) : pen(pen) {}

  void StartWriting()
  {
    pen.Write();
  }

private:
  PenType pen;
};

这允许用户将特定的Pen对象传递给构造函数,如果它没有默认构造函数,或者您不希望它被使用,其次,它仍然允许您省略PenType对象,如果您很高兴让它用默认的构造函数创建一个。 C ++标准库在许多类中都是这样做的(例如,考虑容器类的分配器)。

我删除了继承。 它似乎没有添加任何东西(并且可能会导致问题。您可能不希望Writer类的用户直接调用PenType :: Write函数。您可以使用私有继承,但通常,组合是一个更简单,更传统的设计。

通常,基于策略的设计不需要继承。 将其添加为成员也同样有效。 如果您继续进行继承,请将其设为私有,这样您就不会遇到#4提到的问题。

这看起来像基于策略的智能指针实现的一个很好的例子: 链接 Andrei Alexandrescu在他的一本书中描述了基于策略的智能指针实现。 至于你现在的问题。 我对这些东西有一些经验,但还不足以将我的话理所当然:

广告1和4.我认为基于策略的设计更多是关于模板而不是继承。 您编写模板类,模板参数是策略类,如下所示:

template<class FooPolicy, class BarPolicy>
class Baz {
    // implementation goes here
};

然后使用类中策略类的方法:

void Baz::someMethod(int someArg) {
    FooPolicy::methodInit();
    // some stuff
    BarPolicy::methodDone();
}

我在此示例中使用静态方法,因为策略通常不需要任何状态。 如果是,则按组合合并策略的状态,而不是继承:

template<class FooPolicy, class BarPolicy>
class Baz {
  private:
    FooPolicy::State fooState; // might require 'typename' keyword, I didn't
                               // actually tried this in any compiler
    // rest of the Baz class
};

广告2.您可以编写模板专门化 - 对于主类及其策略的特定组合,您可以编写任何方法或构造函数的特殊版本,AFAIK:

template <>
Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument)
   : fooState(someArgument)
{
    // stuff here
}

希望它对你有所帮助,

麦克风

1 - 此实现看起来是否正确? 我的意思是它真的看起来像一个基于策略的设计?

策略类通过组合行为以产生丰富多样的组合来获得它们的有用性。 当你有一个这样的模板参数时,它就不是一个策略类了。

2 - 我现在可以将任何类型的笔钩到作家身上。 但是当我拿到没有默认构造函数的笔(仅参数化构造函数)时,我会怎么做? 我该如何处理这种情况?

同样,这是政策类的一个奇怪的例子。 但是,要直接回答您的问题,您可以提供一个接受PenType的构造函数。 您还应该避免继承PenType并将其存储为成员(不需要将策略类与其策略紧密耦合)。

我猜编译器会用InkPen替换PenType。 如果是,为什么我不能从StartWriting()调用Write()而不是前缀基类名(PenType :: Write())?

从类模板继承时,必须指定this-> member或BaseClass :: member。

4 - 我认为基于策略的设计会迫使您从语义无效的类派生。 在上面的代码中,只有因为作者使用笔才能从笔中导出作者。 但是说作家是笔在语义上是无效的。 有没有其他更好的方法来解决这个问题,或者我在这里遗漏了什么?

将PenType存储为上面建议的成员。 总是喜欢使用组合继承,因为它避免了继承的紧密耦合关系。

我知道这个帖子已经过时了,但是最初的帖子中存在一个主要缺陷,这个帖子是Google的最佳结果之一...所以:

不要将public继承用于基于策略的设计! 这将说“is-a”而不是“has-a”/“uses-a”。 因此,您应该使用private继承!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM