8

自从我第一次开始学习面向对象编程以来,我一直在努力解决一个问题:应该如何在“正确的”OOP 代码中实现记录器?

我的意思是一个对象,它有一个我们希望代码中的每个其他对象都能够访问的方法;这个方法将输出到控制台/文件/任何东西,我们将使用它来记录——因此,这个对象将是记录器对象。

我们不想将 logger 对象建立为全局变量,因为全局变量不好,对吧?但是我们也不希望在每个对象中调用的每个方法的参数中都传递记录器对象。

在大学里,当我向教授提出这个问题时,他实际上无法给我答案。我意识到实际上有可能实现此功能的包(例如 Java)。不过,我最终要寻找的是如何正确地以 OOP 方式自己实现这一点的知识。

4

16 回答 16

13

确实希望将记录器建立为全局变量,因为全局变量还不错。至少,它们本质上并不坏。记录器是正确使用全局可访问对象的一个​​很好的例子。如果您想了解更多信息,请阅读单例设计模式。

于 2008-09-17T19:26:13.390 回答
4

有一些经过深思熟虑的解决方案。有些涉及绕过OO并使用另一种机制(AOP)。

日志记录并不太适合 OO(这没关系,并非所有事情都如此)。如果你必须自己实现它,我建议只在每个类的顶部实例化“Log”:

私人最终日志=新日志(此);

然后您的所有日志记录调用都是微不足道的: log.print("Hey");

这使得它比单例更容易使用。

让您的记录器找出您传入的类并使用它来注释日志。由于您有一个日志实例,因此您可以执行以下操作:

log.addTag("账单");

并且日志可以为每个条目添加标签账单,以便您可以为您的显示实施更好的过滤。

log4j 和电锯是一个完美的开箱即用的解决方案——如果你不只是学术,使用它们。

于 2008-09-17T19:30:06.190 回答
2

全局可访问的记录器是测试的痛苦。如果您需要“集中式”日志记录工具,请在程序启动时创建它并将其注入需要日志记录的类/方法中。你如何测试使用这样的方法:

public class MyLogger 
{
    public static void Log(String Message) {}
}

你如何用模拟代替它?

更好的:

public interface ILog 
{
    void Log(String message);
}

public class MyLog : ILog 
{
    public void Log(String message) {}
}
于 2008-10-26T22:58:06.563 回答
1

我一直使用单例模式来实现日志对象。

于 2008-09-17T19:25:38.900 回答
0

你可以看看单例模式。

于 2008-09-17T19:24:47.343 回答
0

将记录器创建为单例类,然后使用静态方法访问它。

于 2008-09-17T19:25:01.960 回答
0

我认为您应该为此使用 AOP(面向方面​​的编程),而不是 OOP。

于 2008-09-17T19:26:08.820 回答
0

在实践中,我认为单例/全局方法可以正常工作。最好全局事物只是一个框架,您可以将不同的侦听器(观察者模式)连接到该框架,例如一个用于控制台输出,一个用于数据库输出,一个用于 Windows EventLog 输出等。

不过要小心过度设计,我发现在实践中,只有全局方法的单个类可以很好地工作。

或者你可以使用你工作的特定框架提供的基础设施。

于 2008-09-17T19:26:19.773 回答
0

来自 Microsoft 的 Pattern & Practices 组的Enterprise Library Logging Application Block是在 OOP 环境中实现日志框架的一个很好的例子。他们有一些关于他们如何实现日志应用程序块的很棒的文档,并且所有源代码都可供您自己查看或修改。

还有其他类似的实现:log4net、log4j、log4cxx

他们实现企业库日志记录应用程序块的方式是拥有一个静态Logger类,其中包含许多实际执行日志操作的不同方法。如果您正在查看模式,这可能是单例模式的更好用途之一。

于 2008-09-17T19:28:59.593 回答
0

我完全支持 AOP 和 log4*。这真的帮助了我们。例如,谷歌给了我这篇文章。您可以尝试在该主题上进行更多搜索。

于 2008-09-17T19:33:30.940 回答
0

(恕我直言)“日志记录”如何发生并不是您的解决方案设计的一部分,它更多的是您碰巧在其中运行的任何环境的一部分——比如 Java 中的系统和日历。

您的“好”解决方案是与任何特定日志记录实现尽可能松散耦合的解决方案,因此请考虑接口。我会在此处查看有关 Sun 如何解决此问题的示例,因为他们可能提出了一个非常好的设计并将其全部列出以供您学习!

于 2008-09-17T19:34:03.577 回答
0

使用静态类,它的开销最小,并且可以从简单程序集引用中的所有项目类型访问

请注意,单例是等价的,但涉及不必要的分配

如果您使用多个应用程序域,请注意您可能需要一个代理对象才能从主域以外的域访问静态类

此外,如果您有多个线程,您可能需要锁定日志功能以避免交错输出

恕我直言,单独记录是不够的,这就是我写CALM的原因

祝你好运!

于 2008-09-17T19:34:16.553 回答
0

也许以透明的方式插入日志记录宁愿属于面向方面的编程习语。但我们在这里谈论的是面向对象设计......

在我看来,单例模式可能是最有用的:您可以通过 LoggingService 类的公共静态方法从任何上下文访问 Logging 服务。

尽管这看起来很像一个全局变量,但实际上并非如此:它被适当地封装在单例类中,并不是每个人都可以访问它。这使您即使在运行时也可以更改日志记录的处理方式,但可以保护日志记录的工作不受“vilain”代码的影响。

在我工作的系统中,我们创建了许多 Logging 'singleton',以便能够区分来自不同子系统的消息。这些可以在运行时打开/关闭,可以定义过滤器,可以写入文件......你可以命名它。

于 2008-09-17T19:37:02.680 回答
0

我过去通过将日志类的实例添加到需要访问日志的类的基类(或接口,如果语言支持的话)中解决了这个问题。当您记录某些内容时,记录器会查看当前调用堆栈并从中确定调用代码,设置有关记录语句的正确元数据(源方法、代码行(如果可用)、记录的类等)。类有记录器的数量,记录器不需要专门配置可以自动确定的元数据。

确实会增加相当大的开销,因此它不一定是生产日志记录的明智选择,但是如果您以这种方式设计记录器,则可以有条件地禁用记录器的各个方面。

实际上,我大部分时间都使用公共日志记录(我在 java 中做了很多工作),但是我发现上面描述的设计的某些方面是有益的。拥有一个其他人已经花费大量时间调试的健壮日志系统的好处超过了对可以被认为是更清洁的设计的需求(这显然是主观的,特别是考虑到本文缺乏细节)。

我遇到了导致永久内存问题的静态记录器问题(至少,我认为这就是问题所在),所以我可能很快会重新访问记录器。

于 2008-09-17T19:39:45.247 回答
0

另一种可能的解决方案是使用一个 Log 类来封装日志记录/存储过程。这样你就可以new Log();在需要的时候实例化 a 而不必使用单例。

这是我首选的解决方案,因为如果您通过数据库登录,您需要注入的唯一依赖项是数据库。如果您可能正在使用文件,则不需要注入任何依赖项。您还可以完全避免使用全局或静态日志记录类/函数。

于 2010-04-09T07:32:47.080 回答
0

为了避免全局变量,我建议创建一个全局 REGISTRY 并在那里注册你的全局变量。

对于日志记录,我更喜欢提供一个单例类或提供一些静态方法进行日志记录的类。

实际上,我会使用现有的日志记录框架之一。

于 2010-08-27T22:29:35.677 回答