我可以为我的Logger使用静态类吗?

时间:2011-05-16 23:00:24

标签: php oop

最近我被告知静态类/方法是邪恶的。

以我的班级Logger为例:

class Logger{
   private static $logs = array();
   public static function add($msg){
      self::$logs[]=$msg;
   }

   public static function echo(){
       print_r(self::$logs);
   }
}

我可以随时随地使用我的应用程序:

Logger::add('My log 1');

但是阅读这些开发人员:

Logger类看起来不太好。

所以:我可以静态使用它还是应该不惜一切代价避免使用它?

5 个答案:

答案 0 :(得分:12)

记录类是个例外。

由于它们很少包含很多逻辑,因此您没有相同的测试问题。

日志记录是使用静态类的好地方的完美示例。

想想你的选择:

  • 日志记录对象的全局实例?
  • 单件记录对象?
  • 将日志记录对象传递给每个方法/类(通过构造函数)?

上述情况比使用static进行记录要差得多。

答案 1 :(得分:4)

避免它。我已经看到你的一些帖子现在正在努力解决这个问题,人们会给你不好的建议。我会在我的一些答案/评论中重复我的说法。

在logger类中使用static的方法是将其用作全局访问点。无论什么时候需要logg,你都可以调用Logger :: log()。

1)您无法通过查看您的类定义来判断它取决于Logger类。代码的变化因此变成了冒险:'我希望当我改变这个小小的时候我不会打破一些隐藏的依赖... OOPS!'。

2)测试更难。您无法实际测试使用Logger :: log()向Logger发送消息的类。当测试失败时,你怎么知道它不是因为Logger失败了?你会知道你是否可以用模拟替换它,但在你的情况下,它是不可模仿的。

探索的另一种选择:

使用观察者模式并使Logger成为观察者,需要记录的类可以是可观察的。他们发送消息,如$ this-> observers-> nofify('test succeeded')。

您也可以使用其他形式的事件或依赖注入(自动或手动)。但请不要在方法中调用Logger :: log()。

答案 2 :(得分:2)

我仍然认为日志记录是使用静态类的有效方法。经常说的不可测试的短语如果你做得对,也不是真的。我想实现这个但是没有找到时间,但是,我想到了类似下面的内容。

class Logger {

    protected static $handlerSet = [];

    // Pure static class {{{
        private function __construct() {}
        private function __clone() {}
        private function __sleep() {}
        private function __wakeup() {}
    // }}}

    public static function critical($message, array $context = []) {}

    // You know the PSR drill...

    private static function log($level, $message, array $context) {
        foreach ($this->handlerSet as $handler) {
            $handler->handle($level, $message, $context);
        }
    }

}

当然,我们不希望将处理程序的管理暴露给所有类,因此,我们使用可以访问受保护的处理程序集的子类。

final class LoggingManager extends Logger {

    public static function addHandler(Handler $handler, $name, $level) {
        static::$handlerSet[$name] = $handler;
    }

    public static function removeHandler($name) {
        if (isset(static::$handlerSet[$name])) {
            unset(static::$handlerSet[$name]);
        }
    }

    public static function resetHandlers() {
        static::$handlerSet = [];
    }

    // Other useful stuff...

}

现在测试非常简单,如果你真的想测试像日志这样的东西(可能是你有一些投资回报率,不知道)。

class SomeTest extends YourFrameworksTestCase {

    public function testThatSomethingLogsSomething() {
        try {
            $handler = new TestLogHandler();
            LoggingManager::registerHandler($handler, 'test', 'debug');
            // Test something.
            $this->assertLogRecordExists($handler, '[debug] StackOverflow');
        }
        finally {
            LoggingManager::resetHandlers();
        }
    }

}

还可以创建一个更复杂的测试用例来扩展,为您实现所有日志记录断言。这种方法非常简单,系统中的类不应该关心处理程序是否已注册,也不关心它对记录的消息的作用。这样的事情在你的应用程序中处理,只在那里处理。优势显而易见:

  • 全局访问记录器和日志记录管理器。
  • 易于测试,与依赖注入相当。
  • 无需代码污染DIC解决方案。
  • 始终使用单个记录器实例。
  • ...

答案 3 :(得分:1)

虽然这种方法没有任何问题,但我最近在自己的一个项目中从静态日志记录类方法转移到了log4php

log4php为项目中的每个类使用一个单独的日志记录类实例。在查看该日志框架时,其好处变得显而易见。

记录的消息始终具有上下文(记录消息的类)。这样可以轻松过滤(并使日志稍微有用)。

答案 4 :(得分:0)

静态类的唯一问题是它们很难改变。 所以这里没关系,因为你上课的时间并不多。