有没有人知道是否可以像没有记录器一样的类:
使用单例或全局(la std :: cout)
将实例/指针/引用传递给需要它的每个方法
我以logger类为例,但我的应用程序中有一些类可以从中受益(例如,撤消管理器)。
每个解决方案都存在几个问题:
使用单身人士对于测试是有问题的(还有许多原因,使用单身人士通常不是一个好主意)。全球化也是如此。此外,没有什么可以保证应用程序中只有一个实例,甚至不是一个要求(例如,为什么没有2个记录器?)
将它传递给每个对象构造函数(依赖注入)会导致很多样板代码,并且可能容易出错,因为您必须多次复制/粘贴相同的代码。可以认真考虑在每个类的构造函数中都有一个指向Logger的指针???????
所以我想知道是否有第三种选择,在C ++中,我从未听说过? 对我而言,听起来它需要一些黑魔法引擎盖,但我对我在堆栈溢出中学到的一些技术感到惊喜,这是我在google中找不到的,所以我知道这里有一些真正的大师;)
令人惊讶的是,我发现了许多关于如何设计单身人士的讨论,或者为什么不应该使用单身人士,但我找不到解决我的问题的帖子......
答案 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()操作可能隐藏在作用域后面,并且将存在多线程需要的问题照顾好。但是基本概念可能有效,并且比简单的单例或全局变量更具灵活性。