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

Nav*_*K N 26 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

template<class PenType>
class Writer : public PenType
{
public:
  void StartWriting()
  {
    PenType::Write();
  }
};
Run Code Online (Sandbox Code Playgroud)

3 - 当使用上面的代码时

Writer<InkPen> writer;
Run Code Online (Sandbox Code Playgroud)

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

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

有什么想法吗?

jal*_*alf 25

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

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

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

private:
  PenType pen;
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 继承策略类的原因在*Modern C++ Programming*的第1.6节(丰富的策略)中概述:1)它允许策略将每策略API添加到策略使用类,以及2)如果用户从如果没有,并且仍然具有使用每个策略API的代码,那么编译器会明确地将它们标记为错误,以便它们得到修复.虽然可以将这些每策略API实现为完全外部函数,但将它们作为策略的一部分更为简单和清晰. (13认同)
  • 策略对客户端是可见的,因为客户端决定使用什么策略,因此客户端知道已选择的策略的任何特定行为.例如,需要重新填充的钢笔将由客户重新填充,并且作者不必考虑重新填充. (6认同)
  • 一个带有丰富策略的问题是析构函数:由于存在子类关系,因此可以将策略用户强制转换为策略,然后将其删除,从而导致不正确的删除.通常的解决方案,声明策略的虚拟析构函数,强制通常不需要的虚拟表开销.解决方案是为策略声明受保护的非虚拟(通常为空)析构函数,以防止删除(或实例化)策略对象.这在*Modern C++ Design*的1.7节中有所介绍. (5认同)

Jas*_*siu 14

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

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

template<class FooPolicy, class BarPolicy>
class Baz {
    // implementation goes here
};
Run Code Online (Sandbox Code Playgroud)

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

void Baz::someMethod(int someArg) {
    FooPolicy::methodInit();
    // some stuff
    BarPolicy::methodDone();
}
Run Code Online (Sandbox Code Playgroud)

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

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
};
Run Code Online (Sandbox Code Playgroud)

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

template <>
Baz<SomeConcreteFooPolicy, SomeConcreteBazPolicy>::Baz(someArgument)
   : fooState(someArgument)
{
    // stuff here
}
Run Code Online (Sandbox Code Playgroud)

希望它对你有所帮助,

麦克风


D.R*_*.R. 7

我知道这个帖子很旧,但是最初的帖子有一个重大缺陷,这个帖子是谷歌的最佳结果之一......所以:

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

  • 对于基于策略的设计,拥有公共继承实际上是很常见的。这是为了使各个策略类可以添加宿主类将继承的新方法,并且调用者可以调用这些新方法,而宿主类不必知道这些新方法。虽然这打破了 is-a 的关系,但它确实使一切都更具可扩展性 (11认同)
  • 基于策略的类不是严格 OOP 中的对象。策略类经常代表动词而不是名词。 (3认同)

sti*_*472 5

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

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

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

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

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

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

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

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