这里笔者想讨论一个有关编程风格的问题。它针对一个很常见的编程场景:初始化属性值,如果已配置该属性,初始化为配置的值,否则使用默认值初始化。
来看一个具体的场景:程序带有截图功能,这需要初始化一个截图目录以存储图片。如果配置指定了截图目录路径,则按配置初始化截图目录,否则初始化为默认目录。
这里假设有两个属性,一个是 captureDir
代表截图目录路径的字符串表示(来源于配置文件),另一个是 captureDirectory
代表截图目录对应的 File
对象。
典型的获取截图目录 File
对象的方法实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public File getCaptureDirectory() { if (captureDirectory != null) { return captureDirectory; }
if (captureDir == DEFAULT_SCREENSHOT_DIR) { captureDirectory = new File(workDirectory, captureDir); } else { captureDirectory = new File(captureDir); }
if (!captureDirectory.exists()) { boolean made = captureDirectory.mkdirs(); if (!made) { String errMsg = "截图文件夹创建失败:" + captureDirectory; captureDirectory = null; throw new ConfigException(errMsg); } }
return captureDirectory; }
|
代码逻辑很简单:判断 captureDirectory
是否已初始化,如是直接返回,否则执行初始化逻辑。由于进行了“非空”判断,姑且叫它“非空拦截法”吧。
反过来,我们也可以“判空”,再执行初始化,叫“判空初始化法”好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public File getCaptureDirectory() { if (captureDirectory == null) { if (captureDir == DEFAULT_SCREENSHOT_DIR) { captureDirectory = new File(workDirectory, captureDir); } else { captureDirectory = new File(captureDir); }
if (!captureDirectory.exists()) { boolean made = captureDirectory.mkdirs(); if (!made) { String errMsg = "截图文件夹创建失败:" + captureDirectory; captureDirectory = null; throw new ConfigException(errMsg); } } }
return captureDirectory; }
|
除此之外,Java8 引入的 Optional
类也很适合处理这种场景——叫“Optional初始化法”吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public File getCaptureDirectory() { return Optional.ofNullable(captureDirectory) .orElseGet( () -> { if (captureDir == DEFAULT_SCREENSHOT_DIR) { captureDirectory = new File(workDirectory, captureDir); } else { captureDirectory = new File(captureDir); }
if (!captureDirectory.exists()) { boolean made = captureDirectory.mkdirs(); if (!made) { String errMsg = "截图文件夹创建失败:" + captureDirectory; captureDirectory = null; throw new ConfigException(errMsg); } }
return captureDirectory; }); }
|
这样,针对带默认值的属性初始化场景,就有三种编写方法了:
- 非空拦截法
- 判空初始化法
- Optional 初始化法
那用哪个呢?
我们从代码量和缩进风格两个维度来比较一下三个方法。
从代码量上说,三个方法差异不大,从示例看只有两行的差异。
从缩进风格看,非空拦截法完全没有额外缩进,是三者中最“平”的;判空初始化法包围了一层 if
语句,多了一级缩进;Optional 初始化法由于链式调用以及 lambda
表达式又多了一级缩进。
小结
由于代码量差异较小,所以这里单论缩进效果来选择使用哪种方法。
首选当然是“非空拦截法”,它将代码量最小的逻辑分支前置处理,逻辑上更为清晰。
其次“判空初始化法”,它将逻辑分支不同部分“隔离”处理,“共享”相同部分,减少了代码量。
最后“Optional 初始化法”,它的缩进较多,如果主体逻辑代码也有较多的缩进,那就不太美观了。另外,如果 lambda
表达式代码太多,就会显得很混乱。但是,Optional
的链式代码有一个优点,即逻辑语义更为清晰。代码量较少的情况下也是一个不错的选择。