最近我被告知静态类/方法是邪恶的。
以我的班级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类看起来不太好。
所以:我可以静态使用它还是应该不惜一切代价避免使用它?
答案 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();
}
}
}
还可以创建一个更复杂的测试用例来扩展,为您实现所有日志记录断言。这种方法非常简单,系统中的类不应该关心处理程序是否已注册,也不关心它对记录的消息的作用。这样的事情在你的应用程序中处理,只在那里处理。优势显而易见:
答案 3 :(得分:1)
虽然这种方法没有任何问题,但我最近在自己的一个项目中从静态日志记录类方法转移到了log4php。
log4php为项目中的每个类使用一个单独的日志记录类实例。在查看该日志框架时,其好处变得显而易见。
记录的消息始终具有上下文(记录消息的类)。这样可以轻松过滤(并使日志稍微有用)。
答案 4 :(得分:0)
静态类的唯一问题是它们很难改变。 所以这里没关系,因为你上课的时间并不多。