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

发布于 2024-07-20 16:48:56 字数 1316 浏览 14 评论 0原文

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 - 我现在可以将任何类型的笔连接到 writer。 但是,当我得到一支没有默认构造函数(只有参数化构造函数)的笔时,我该怎么办? 我将如何处理这种情况?

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

3 - 当使用上面的代码时,

Writer<InkPen> writer;

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

4 - 我认为基于策略的设计迫使您从语义上无效的类中派生。 在上面的代码中,writer 是从 pen 派生出来的,只是因为 writer 使用了 pen。 但说作家是一支笔在语义上是无效的。 还有其他更好的方法来解决这个问题还是我在这里遗漏了一些东西?

有什么想法吗?

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;
}

I wrote the above code as part of learning policy based designs. I have few questions on the above code

1 - Does this implementation look correct? I mean: does it really look like a policy based design?

2 - I can now hook any kind of pens to writer. But what will I do when I got a pen with no default constructor (only parameterized constructors)? How will I handle this situation?

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

3 - When the above code is used like

Writer<InkPen> writer;

I guess compiler will replace PenType with InkPen. If yes, why I am not able to call just Write() from StartWriting() instead of prefixing base class name (PenType::Write())?

4 - I think policy based design forces you to derive from classes which is semantically invalid. In the above code, a writer is derived from a pen only because writer uses a pen. But saying writer is a pen is semantically invalid. Is there any other better way to address this or I am missing something here?

Any thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

完美的未来在梦里 2024-07-27 16:48:57

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

广告 1 和广告 2。 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
};

Ad 2.您可以编写模板专业化 - 对于主类及其策略的特定组合,您可以编写任何方法或构造函数的特殊版本,据我所知:

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

希望这对你有一点帮助,

迈克

This looks like a nice example of policy-based smart pointer implementation: link. Andrei Alexandrescu describes policy-based smart pointer implementation in one of his books. As to your questions now. I have some experience in this stuff but not enough to take my words for granted:

Ad 1 & 4. I guess policy-based design is more about templates than inheritance. You write a template class and template arguments are policy classes, like that:

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

Then you use methods from policy classes in your class:

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

I use static methods in this example because often policy doesn't require any state. If it does, you incorporate policy's state by composition, not by inheritance:

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
};

Ad 2. You can write a template specialization - for a particular combination of main class and it's policies you can write a special version of any method or constructor, AFAIK:

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

Hope it helps you a bit,

Mike

各自安好 2024-07-27 16:48:57

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

策略类的有用性来自于组合行为以产生丰富的组合。 当您拥有像这样的单个模板参数时,它就不再是一个策略类。

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

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

我猜编译器会取代 PenType
用墨水笔。 如果是的话,为什么我不能
只调用 Write()
StartWriting() 而不是前缀
基类名称(PenType::Write())?

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

4 - 我认为基于政策的设计力量
你从类中派生出
语义上无效。 在上面的
代码,作家源于笔
只是因为作家使用笔。 但
说作家是一支笔在语义上是正确的
无效的。 还有其他更好的办法吗
解决这个问题,否则我就失踪了
这里有什么东西吗?

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

1 - Is this implementation looks
correct? I mean is it really looks
like a policy based design?

Policy classes derive their usefulness from combining behaviors to produce a rich variety of combinations. When you have a single template parameter like this, it's not much of a policy class.

2 - I can now hook any kind of pens to
writer. But what will I do when I got
a pen with no default constructor
(only parameterized constructors)? How
will I handle this situation?

Again, this is an odd example of a policy class. However, to directly answer your question, you can provide a constructor which accepts PenType. You should probably also avoid inheriting from PenType and store it as a member instead (no need to tightly couple your policy class with its policies).

I guess compiler will replace PenType
with InkPen. If yes, why I am not able
to call just Write() from
StartWriting() instead of prefixing
base class name (PenType::Write())?

When you inherit from a class template, you have to specify this->member or BaseClass::member.

4 - I think policy based design forces
you to derive from classes which is
semantically invalid. In the above
code, a writer is derived from a pen
only because writer uses a pen. But
saying writer is a pen is semantically
invalid. Is there any other better way
to address this or I am missing
something here?

Store PenType as a member as suggested above. Always prefer composition to inheritance as it avoids the tight coupling relationship of inheritance.

书信已泛黄 2024-07-27 16:48:57

我知道这个线程很旧,但是在最初的帖子中有一个重大缺陷,并且这个线程是 Google 的顶级结果之一......所以:

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

I know this thread is old, but there is a major flaw in the initial post and this thread is one of the top results of Google...so:

Do not use public inheritance for policy-based design! This would say "is-a" instead of "has-a" / "uses-a". You should therefore use private inheritance!

晚风撩人 2024-07-27 16:48:56

这是我实现该类的方式:

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.

Here's how I would implement the class:

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

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

private:
  PenType pen;
};

This allows the user to pass a specific Pen object to the constructor, if it either doesn't have a default constructor, or you don't want it to be used, and second, it still allows you to omit the PenType object if you're happy to let it create one with the default constructor. The C++ standard library does the same in many classes (think of the allocators for container classes for example).

I removed the inheritance. It didn't really seem to add anything (and might cause problems. You probably don't want the user of the Writer class to call the PenType::Write function directly. You could use private inheritance instead, but often, composition is a simpler and more conventional design.

In general, policy-based design does not require inheritance. Adding it as a member works just as well. If you do go for inheritance, make it private so you don't get the problem you mentioned as #4.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文