【问题标题】:Applying policy based design question应用基于策略的设计问题
【发布时间】:2010-04-05 19:59:25
【问题描述】:

我没有读过 Modern C++ Design 这本书,但发现通过模板进行行为注入的想法很有趣。我现在正在尝试自己应用它。

我有一个类,它有一个我认为可以作为策略注入的记录器。记录器有一个 log() 方法,该方法根据其策略采用 std::string 或 std::wstring:

// basic_logger.hpp
template<class String>
class basic_logger
{
public:
    typedef String string_type;

    void log(const string_type & s) { ... }
};
typedef basic_logger<std::string> logger;
typedef basic_logger<std::wstring> wlogger;

// reader.hpp
template<class Logger = logger>
class reader
{
public:
    typedef Logger logger_type;

    void read()
    {
        _logger.log("Reading...");
    }

private:
    logger_type _logger;
};

现在的问题是,读者应该像上面那样将 Logger 作为参数,还是应该使用 String 然后将 basic_logger 实例化为实例变量?像这样:

template<class String>
class reader
{
public:
    typedef String string_type;
    typedef basic_logger<string_type> logger_type;

    // ...

private:
    logger_type _logger;
};

正确的方法是什么?

【问题讨论】:

  • 请少用注入的东西——这是 C++。
  • 我知道我对 Java 有点失望(我主要使用它,但不是它的忠实粉丝)。行为和状态仍然需要以某种方式注入(或任何你想调用的方式),无论是 C++、Ruby 还是其他方式。根据我对 Boost 的理解,对于 C++,基于策略的设计是必经之路。我只是想绕开它。
  • 实际阅读 Modern C++ Design 可能对您有最大的帮助,因为 Alexandrescu 展示了与此类似的示例。

标签: c++ policy-injection


【解决方案1】:

要真正使用策略类,策略需要是模板参数。一个例子是 basic_string 的 char_traits 参数,尽管它的实现方式与 MC++D 的策略不同,后者使用继承来利用空基类优化并允许轻松添加到类的公共接口(以比包装每个可能的方法;再次阅读 MC++D)。您仍然可以提供默认值:

template<class String, class Logger=basic_logger<String> >
struct reader : Logger {
  void read() {
    this->log("Reading...");
  }
};

【讨论】:

  • 我想你说得对,我应该读 Alexandrescu 的书。我从书中遇到了一些例子,但是如果没有这本书,很难将这些例子放到上下文中。例如,我不知道空基类优化。无论如何,我想这回答了我的问题。谢谢!
【解决方案2】:

问题是应该根据其记录器的类型对阅读器进行参数化,还是根据它读取的内容的类型对阅读器进行参数化?如果这是问题,我会认为答案是显而易见的——后者。

恕我直言,这个问题的问题在于 String 和 Logger 实际上都不是策略。一个策略在编译时说明了像记录器这样的东西应该如何进行记录——你的代码只是向阅读器提供一种记录器,这同样可以在运行时使用继承来完成。

【讨论】:

  • 你是对的。在这种情况下,记录器最好在运行时设置。但是,如果您将记录器视为记录策略,我认为 Roger 回答了我上面的问题。正如我之前所说,我仍在努力思考政策......
【解决方案3】:

策略通常是影响类行为的参数。

从一个类中提取策略实际上是相当困难的,更困难的是策略需要涵盖正交概念,以便您可以在不影响其他概念的情况下更改一个......正如您可以想象的那样,这非常具有挑战性。

如果您想查看策略使用的良好示例,请查看本书中完整演示的Loki::Singleton

template
<
  typename T,
  template <class> class CreationPolicy = CreateUsingNew,
  template <class> class LifetimePolicy = DefaultLifetime,
  template <class, class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
  class MutexPolicy = LOKI_DEFAULT_MUTEX
>
class SingletonHolder;

令人印象深刻,不是吗?

基于策略的设计原则是您尝试分解班级的各种动作,以便您可以独立推理它们。

好吧,现在我必须承认我对要求具有一组模板参数的类的想法不太满意,我个人更喜欢这样的东西:

template
<
  class T,
  class CreationPolicy = CreateUsingNew<T>,
  class LifetimePolicy = DefaultLifeTime<T>,
  class MutexPolicy = LOKI_DEFAULT_MUTEX,
  template <class, class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL
>
class SingletonHolder;

最后一个真的没办法,你应该把SingletonHolder类本身传递给它。

但是我发现在这里交换策略更容易,它允许我定义如下策略:

template <class T, size_t Param> MyCreationPolicy;

并直接使用,而不必为给定的参数值包装它,以便它与签名匹配。

【讨论】:

  • 感谢您的澄清!很有帮助!
猜你喜欢
  • 1970-01-01
  • 2011-04-08
  • 2016-02-20
  • 2014-09-30
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多