设计配置选项等级系统
- 什么是“配置选项等级”呢?
举个例子,PHP设置报错级别(来源PHP官网示例:https://www.php.net/manual/zh/function.error-reporting.php):
1 <?php 2 3 // 关闭所有PHP错误报告 4 error_reporting(0); 5 6 // Report simple running errors 7 error_reporting(E_ERROR | E_WARNING | E_PARSE); 8 9 // 报告 E_NOTICE也挺好 (报告未初始化的变量 10 // 或者捕获变量名的错误拼写) 11 error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE); 12 13 // 除了 E_NOTICE,报告其他所有错误 14 error_reporting(E_ALL ^ E_NOTICE); 15 16 // 报告所有 PHP 错误 (参见 changelog) 17 error_reporting(E_ALL); 18 19 // 报告所有 PHP 错误 20 error_reporting(-1); 21 22 // 和 error_reporting(E_ALL); 一样 23 ini_set('error_reporting', E_ALL); 24 25 ?>
可以看到我们设置错误级别非常方便,想要哪些错误级别直接用 “|” 链接即可;
Java中网络编程,使用的 java.nio.channels.SelectionKey 中设置 Interest Option 也是类似操作:
tmpKey.interestOps(tmpKey.interestOps() | SelectionKey.OP_READ);
打开 java.nio.channels.SelectionKey 源码发现定义了
OP_READ = 1
OP_WRITE = 4
OP_CONNECT = 8
……
等类常量;
再向下翻,发现一段代码:
public final boolean isReadable() { return (this.readyOps() & 1) != 0; }
原来前面通过设置interestOps后,判断channel是否可读就是判断:(this.readyOps() & 1) != 0;
原理也很简单,二进制操作 | 相当于加,即对应的二进制位变更为1,&操作即为 判断是否有相同位置均为1;
例如:
1 | 2 得 3,
1二进制 0001
2 二进制 0010
或之后,只要对应位置任意一位为 1, 即将该位置变更为1;
3 二进制 0011
得到3后,判断用户是否设置了 常量值为1对应的选项,即为:
3 & 1
3 二进制 0011
1 二进制 0001
与之后,对应位置均为1得1否则得0:
0001 十进制(1), 1!= 0 成立,返回true,说明用户设置了 常量值为1对应的选项!
那么如何选择 选项常量值呢?可以看到Java SelectionKey 设置的是依次是 1、4、 8 ...
实际上因为十进制数二进制或操作可以看做是 两个数之间 的加和!
如 1 | 2 == 3, 2 | 4 == 6
所以选取选项的值,原则遵循 下一个选项值比前面所有选项值加和大即可;
例如可以这样设计:
0 E_NONE 1 E_NOTICE 2 E_WARN 4 E_ERROR 8 E_ALL
0 < 1
0 + 1 < 2;
0 + 1 + 2 = 3 < 4
0 + 1 + 2 + 4 = 7 < 8
以此类推
设置选项时,比如想要E_ALL登记,却要排除E_NOTICE, 直接 E_ALL & ~E_NOTICE 即可!
用Java编写一个简单的错误信息上报功能组件:
1 public class ReportLevel { 2 public static final int E_NONE = 0; 3 public static final int E_NOTICE = 1; 4 public static final int E_WARNING = 2; 5 public static final int E_ERROR = 4; 6 7 // all notice/warning/error exception or error 8 public static final int E_ALL = 7; 9 10 public static int REPORT_LEVEL; 11 12 public static void setReportLevel(int reportLevel) { 13 REPORT_LEVEL = reportLevel; 14 } 15 16 public static boolean shouldReportNotice() { 17 return (REPORT_LEVEL & E_NOTICE) != 0; 18 } 19 20 public static boolean shouldReportWarning() { 21 return (REPORT_LEVEL & E_WARNING) != 0; 22 } 23 24 public static boolean shouldReportError() { 25 return (REPORT_LEVEL & E_ERROR) != 0; 26 } 27 }
测试使用:
1 public class ReportLevelTest { 2 3 @Test 4 public void testAddLevel() { 5 ReportLevel.setReportLevel(ReportLevel.E_WARNING | ReportLevel.E_ERROR); 6 7 Assert.assertTrue(ReportLevel.shouldReportWarning()); 8 Assert.assertTrue(ReportLevel.shouldReportError()); 9 10 Assert.assertFalse(ReportLevel.shouldReportNotice()); 11 } 12 13 @Test 14 public void testExcludeLevel() { 15 // 写为 ReportLevel.E_ALL ^ ReportLevel.E_NOTICE 也可以 16 // ReportLevel.setReportLevel(ReportLevel.E_ALL & ~ReportLevel.E_NOTICE); 17 ReportLevel.setReportLevel(ReportLevel.E_ALL ^ ReportLevel.E_NOTICE); 18 19 Assert.assertTrue(ReportLevel.shouldReportWarning()); 20 Assert.assertTrue(ReportLevel.shouldReportError()); 21 22 Assert.assertFalse(ReportLevel.shouldReportNotice()); 23 } 24 25 @Test 26 public void testNone() { 27 ReportLevel.setReportLevel(ReportLevel.E_NONE); 28 29 Assert.assertFalse(ReportLevel.shouldReportWarning()); 30 Assert.assertFalse(ReportLevel.shouldReportError()); 31 Assert.assertFalse(ReportLevel.shouldReportNotice()); 32 } 33 }