c ++:没有全局或单例的logger类或将其传递给每个方法

时间:2011-05-04 02:24:49

标签: c++ logging dependency-injection singleton

有没有人知道是否可以像没有记录器一样的类:

  • 使用单例或全局(la std :: cout)

  • 将实例/指针/引用传递给需要它的每个方法

我以logger类为例,但我的应用程序中有一些类可以从中受益(例如,撤消管理器)。

每个解决方案都存在几个问题:

  • 使用单身人士对于测试是有问题的(还有许多原因,使用单身人士通常不是一个好主意)。全球化也是如此。此外,没有什么可以保证应用程序中只有一个实例,甚至不是一个要求(例如,为什么没有2个记录器?)

  • 将它传递给每个对象构造函数(依赖注入)会导致很多样板代码,并且可能容易出错,因为您必须多次复制/粘贴相同的代码。可以认真考虑在每个类的构造函数中都有一个指向Logger的指针???????

所以我想知道是否有第三种选择,在C ++中,我从未听说过? 对我而言,听起来它需要一些黑魔法引擎盖,但我对我在堆栈溢出中学到的一些技术感到惊喜,这是我在google中找不到的,所以我知道这里有一些真正的大师;)

令人惊讶的是,我发现了许多关于如何设计单身人士的讨论,或者为什么不应该使用单身人士,但我找不到解决我的问题的帖子......

3 个答案:

答案 0 :(得分:10)

我想你可以做一些类似于使用Log4j包在Java中完成的事情(可能是用它的Log4c版本完成的):

有一个可以返回多个记录器实例的静态方法:

Logger someLogger = Logger.getLogger("logger.name");

getLogger()方法未返回单个对象。它返回命名的记录器(必要时创建它)。 Logger只是一个接口(在C ++中它可能是一个完全抽象的类) - 调用者不需要知道正在创建的Logger个对象的实际类型。

你可以继续模仿Log4j并且重载getLogger()也需要一个工厂对象:

Logger someLogger = Logger.getLogger("logger.name", factory);

该调用将使用factory来构建记录器实例,使您可以更好地控制正在创建的基础Logger对象,这可能有助于您进行模拟。

因此,不需要将任何内容传递给您自己代码的构造函数,方法等。您只需在需要时抓取所需的名为Logger并登录即可。根据您编写的日志记录代码的线程安全性,您可以让getLogger()返回的是类的静态成员,因此您只需每个类调用一次getLogger()

答案 1 :(得分:6)

带有一些静态方法的类怎么样?

class Logger
{
public:
  static void record(string message)
  {
    static ofstream fout("log");
    fout << message << endl;;
  }
  ...
};

...

void someOtherFunctionSomewhere()
{
  Logger::record("some string");
  ...
}

没有Singleton,没有全局变量,但任何可以看到Logger.h的代码都可以调用成员函数,如果所有公共成员函数都返回void,那么很容易将其用于测试

答案 2 :(得分:0)

我不认为C ++中有一个很好的选择,但Emacs Lisp中有一个值得考虑的问题:dynamic binding of variables。基本概念是这样的,当您引用变量时,您将不一定通过名称访问全局变量,而是最后在同一名称的执行路径中定义的变量。在伪C ++代码中,这将是这样的:

// Pseudo-C++ with dynamic binding
Logger logger = Logger("GlobalLogger");

void foo() { logger.log("message"); }

int main() 
{
   foo(); // first
   {
      Logger logger = Logger("MyLogger");
      foo(); // second
   }
   foo(); // third
}

在这个伪C ++示例中,当您第一次调用foo()时,将使用GlobalLogger,当您第二次调用它时,将调用MyLogger,因为MyLogger将覆盖全局变量as只要它在范围内,即使在foo()内。当MyLogger超出范围时,第三次调用将返回GlobalLogger。这允许使用自定义的代码覆盖全局记录器,而不必通过所有代码传递记录器对象,而无需设置全局变量。

Real C ++没有动态绑定,但应该可以复制它的各个方面:

std::stack<Logger> logger = { Logger("GlobalLogger") };

void foo() { logger.top().log("message"); }

int main()
{
    foo();
    {
      logger.push(Logger("MyLogger"));
      foo();
      logger.pop();
    }
    foo();
}

为了进一步清理,堆栈应隐藏在DynamicBinding类中,手动.push()/。pop()操作可能隐藏在作用域后面,并且将存在多线程需要的问题照顾好。但是基本概念可能有效,并且比简单的单例或全局变量更具灵活性。