上篇文章中,我们提到,面向过程的代码无法利用配置来减少调用时期的冗余代码,面向对象的编程方式似乎是一种解决方案。在本篇文章中我们利用面向对象的编程方式来看下它是如何做到减少重复代码的。
版本五(改头换面新设计)甲方:我就不说啥了,你都把我要说的都说完了,你看着改吧。
之前说到,对象可以减少过程式代码中参数的配置问题,那么怎么才能做到呢?因为在每次调用函数时都需要将相关参数传入,那么我们可以将参数首先保存在类对象中,为此,我们需要一个新的类,按照v4版本中的LogToConsoleAndFiles的调用参数,我们将logToConsole以及logFilePaths两个参数由函数的输入参数提升到类成员字段里,并通过构造函数的方式进行赋值,如下所示。
public class Logger { private bool _logToConsole; private string[] _logFilePaths; public Logger(bool logToConsole = false, string[] logFilePaths = null) { _logToConsole = logToConsole; _logFilePaths = logFilePaths; } ... }这样一来,只要我们保持了对Logger类对象的引用,那么自然可以利用这两个字段中的值。接下来,我们定义日志记录的 API 方法。
public class Logger { ... public void LogToTarget(LogLevel level, string message) { var logData = new LogData(level, message); if (_logToConsole) LogToConsole(logData); if (_logFilePaths != null) LogToFiles(logData); } private void LogToConsole(LogData logData) { Console.WriteLine(logData); } private void LogToFiles(LogData logData) { foreach(string path in _logFilePaths) { using var fs = new FileStream(path, FileMode.Append); using var sw = new StreamWriter(fs); sw.WriteLine(logData); } } ... }这里,和 v1~v4 版本中的LogHelper静态类中的相关函数逻辑一样,只不过暴露出的API方法改名为LogToTarget。在LogToTarget方法中,我们通过开关来控制我们将日志记录到哪,它不负责具体的写入逻辑,具体写入部分由之后两个私有方法处理。这样的方法架构比较遵循单一职责的原则,在单一职责的原则中,每个函数只负责一个功能。这里,LogToTarget函数作为API方法由外界进行调用调用,LogToConsole函数负责向控制台写入日志,LogToFile函数负责向多个文件写入。值得一提的是,我们改变了API方法的名称,不过无所谓了,本身的使用方式就已经发生大变化了,就不要在意这些小的兼容性。反正甲方的脾气好,兼容之前版本的问题不是我们要考虑的,再加上咱们又不是真正意义上的开源项目,只是一个小的 demo。另外,既然有新的入口,那么旧的入口类LogHelper就没有存在的必要了,也就一并删了吧。
对于v5版本,日志记录的使用方式就发生了变化。对于上回的使用场景,他就变成了这样:
var log = new Logger(true, new string[] { "./log.txt" }); log.LogToTarget(LogLevel.Information, "正在登陆..."); log.LogToTarget(LogLevel.Information, "正在验证账号合法性..."); log.LogToTarget(LogLevel.Information, "正在验证密码是否正确..."); log.LogToTarget(LogLevel.Information, "登陆完成");可以看到,和之前的写法相比,这种写法简便了不少,但是仍发现有部分冗余的代码,LogLevel.Information这玩意需要你对这个类库有一定了解才会使用的,在你不接触这个类库前,你并不知道这里需要填什么,虽然有VS强大的智能提示,但是仍还是有一定的心理负担,就不能像之前提供默认值的方式么?这里,这里有一个更好的方式,我们对LogToTarget不同等级的日志记录做进一步的封装。将6个等级封装成6个函数。
public class Logger { ... public void LogVerbose(string message) => LogToTarget(LogLevel.Verbose, message); public void LogDebug(string message) => LogToTarget(LogLevel.Debug, message); public void LogInformation(string message) => LogToTarget(LogLevel.Information, message); public void LogWarning(string message) => LogToTarget(LogLevel.Warning, message); public void LogError(string message) => LogToTarget(LogLevel.Error, message); public void LogFatal(string message) => LogToTarget(LogLevel.Fatal, message); }我们将不同等级的日志写入逻辑进一步封装。这样,当你需要写入某个特定等级的日志时通过这些 API 方法写入只需要提供日志字符串即可,进一步降低了使用门槛。比如说,原先的调用方式进一步简化成这样的写法。
var log = new Logger(true, new string[] { "./log.txt" }); log.LogInformation("正在登陆..."); log.LogInformation("正在验证账号合法性..."); log.LogInformation("正在验证密码是否正确..."); log.LogInformation("登陆完成"); 版本六(扩展,无限的可能)在v5版中,甲方发现现在的类库用起来还不错,使用方便,调用方式符合直觉。不过……