日志语句阻止重构:如何帮助解决这个问题?

时间:2010-07-28 14:43:01

标签: logging refactoring legacy

我有一些相对较大的遗留方法,我想重构。它符合Michael Feathers的“有效使用遗留代码”中指定的“项目符号方法”类型,因此可以以相当直接的方式分成几个顺序方法。但是它的每个连续步骤都输出一些日志消息,并且形成该消息需要比步骤本身更多的数据。因此,当我尝试提取方法时,我最终得到了具有6个参数的方法。如果我删除了那些日志语句,我将只有一个参数的方法。所以我实际上无法重构任何东西。我不允许删除日志语句。

方法的一部分看起来像是:

// much of code before
Device device = getDevice(deviceID);
boolean isFirstRegistration = false;

if (device == null) {
    /*logger.trace(
            "DeviceId", deviceID,
            "ADM", adminCode,
            "Phone", clientData.getPhone()
    );
    logger.info("First registration of the device. Device ID - " + deviceID);*/
    isFirstRegistration = true;
} else {
    /*logger.trace(
            "DeviceId", deviceID,
            "ADM", adminCode,
            "Phone", clientData.getPhone()
    );
    logger.info("Device ID - " + deviceID
            + " has been previously registered by adminCode: "
            + device.getAdminCode());*/
}
// much of code after

如您所见,注释掉了日志记录语句。在这种情况下,我可以提取方法boolean isFirstRegistration(String deviceId)。但是当它们被取消注释时,签名膨胀到boolean isFirstRegistration(String deviceId, String adminCode, ClientData clientData)。这不是最极端的情况,只是第一眼看到的情况。你有什么想法我应该如何重构这种方法?

2 个答案:

答案 0 :(得分:6)

萌芽课。将日志记录转换为帮助程序类,并根据需要为其提供所需的所有数据。

更新:使用显示的示例变量,我会在设备填充后立即调用myLogger.setDevice(device);类似于adminCode,clientData等。给出traceDeviceAdminCodeAndPhone()logFirstRegistration()之类的记录器日志方法,它使用自己的实例变量。变量发生变化的任何地方,再将它们反馈给记录器。现在,将记录器传递给您正在提取的方法,以及新方法直接需要的任何参数(但不能更多),并且您的记录器仍然可以在提取的方法中报告它需要的内容。

此外,如果开始看起来你的记录器与你的方法过于亲密,另一种方法是将方法提取到一个新类并将一些局部变量转换为实例变量;那么记录器可以简单地向新类询问值而不是自己保留它们。但作为助手的记录器类可能是一个更小,影响更小的重构。无论好坏取决于你想要去哪里。

答案 1 :(得分:3)

卡尔的答案肯定是有效的,但我认为至少还有另一个不错的选择。

某些日志记录框架支持所谓的Mapped Diagnostic Context。您可以在其中存储键值对,然后框架可以按照您在配置文件中指定的格式将它们添加到日志行中。由于您似乎通常记录相同的数据(至少在跟踪中),我认为它非常适合您的需求。

支持它的一些框架是Log4J,SLF4J,但我想在“非Java”世界中也应该存在类似的东西。

我认为你至少可以使用它来代替跟踪消息,而且会获得很多收益:

  • 少,清洁代码
  • 无需传递仅用于记录
  • 的参数
  • 统一记录的消息(根据您提供的模式由框架格式化)
  • 更灵活 - 在记录配置(它所属的位置)中控制记录的内容/时间/方式。

有一点需要注意:键/值对通常存储在线程局部变量中,这会导致一些可能的陷阱:

  • 如果频繁切换线程,它可能无法正常工作/不易使用
  • 通常清理(删除不需要的值)是您的任务,而不是由框架自动完成。