所以昨天我有一个日志文件,在提供的log.txt中有逗号分隔的条目,如下所示:
entry1.1,entry1.2,entry1.3
entry2.1,entry2,2,entry2.3
..........................
因此,幸运的是,我继续创建了一个案例类:
case class LogEntry(
entry1:String,
entry2:String,
entry3:String
)
在阅读案例类时填充案例类如下:
line.split match {
case Array(entry1,entry2,entry3) => LogEntry(entry1,entry2,entry3)
}
现在当我运行我的代码时出现了问题,我注意到没有创建LogEntry对象。
我看了今天提供给我的log.txt,并意识到条目已经改变:
我现在有:
entry1.1,entry1.2,entry1.3,entry1.4
entry2.1,entry2,2,entry2.3,entry2.4
...................................
我现在每行都有第四个条目。好吧,这似乎没什么大不了的,只需用第四个条目(代码气味1)更改我的case类,然后更改模式匹配(代码气味2)
有人可以建议我应该如何编写代码来处理这种情况。我想扩展我的代码而不是修改它。
由于
答案 0 :(得分:2)
当你想要正确地抽象日志行时要做的第一件事是通过名称(即entry1,entry2等)给出单个元素名称和正确的类型而不是伪索引。所以我们将:
case class LogEntry(
time: java.util.Date,
userId: Int,
host: String
)
输入的严格程度取决于您的确切用例(请注意host
此处不是java.net.InetAddress
)。
当新列添加到日志文件中时,您可能希望保留两件事:
案例1很简单。只需将该字段添加到案例类:
case class LogEntry(
time: java.util.Date,
userId: Int,
host: String,
port: Int
)
由于你有名字,你甚至可以改变字段的顺序,旧代码仍然可以工作,因为它预期的字段仍在这里。由于您不想再处理旧的日志文件,只需调整读入代码即可。
案例2有点棘手:您必须反映在运行的应用程序中将包含旧的和新的日志条目这一事实。你可以:
使其他字段可选,并提供默认值:
case class LogEntry(
time: java.util.Date,
userId: Int,
host: String,
port: Option[Int] = None
)
在层次结构中反映日志格式的版本(注意:您不应该继承案例类,因此您必须从中创建普通类):
class LogEntry(
val time: java.util.Date,
val userId: Int,
val host: String
)
class ExtendedLogEntry(
time: java.util.Date,
userId: Int,
host: String,
val port: Int
) extends LogEntry(time, userId, host)
选项1允许处理日志条目的代码处理条目类型之间的差异(即所有函数仍然是LogEntry => ?
类型)。还要注意,它可能允许不一致的日志条目(添加两个字段,格式1中都不存在,格式2中需要,您仍然可以将其中一个设置为None
)。
选项2允许调用代码处理条目类型之间的差异。如果高级函数需要ExtendedLogEntry
,则其类型将为ExtendedLogEntry => _
,因此您无法向该函数提供LogEntry
(替代方法:函数中的模式匹配并提供默认/回退行为) 。此外,它可以防止选项1可能出现的不一致。
关于读入:选项1将在读取旧式日志文件时自动设置默认参数,在选项2中,旧式日志文件将像以前一样生成LogEntry
。因此,在这两种情况下,旧的读入代码不需要更改,但可能会进行调整以检测新样式或旧样式日志并生成适当的条目。 (请注意,这可能是选项2的问题,因为如果您不想进行转换,则通过打字系统静态强制执行日志的样式。)