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

时间:2009-05-16 15:52:44

标签: c++ design-patterns policy-injection policy-based-design

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;

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

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

有什么想法吗?

4 个答案:

答案 0 :(得分:24)

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

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。

答案 1 :(得分:12)

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

Ad 1&amp;我认为基于策略的设计更多是关于模板而不是继承。您编写模板类,模板参数是策略类,如:

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
}

希望它对你有所帮助,

麦克

答案 2 :(得分:5)

  

1 - 这个实现看起来如何   正确?我的意思是它真的看起来   像基于政策的设计?

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

  

2 - 我现在可以挂钩任何类型的笔   作家。但是当我得到时,我会怎么做   没有默认构造函数的笔   (只有参数化构造函数)?怎么样   我会处理这种情况吗?

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

  

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

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

  

4 - 我认为基于政策的设计力量   你从派生的派生出来的   语义无效。在上面   代码,作家是从笔中派生出来的   只是因为作家使用钢笔。但   说作家是笔是语义上的   无效。还有其他更好的方法吗?   解决这个或我失踪   什么东西在这里?

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

答案 3 :(得分:3)

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

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