劣质代码的产生原因(2)编程语言知识不足 (续2)
3.正则表达式
a. 白名单
下列代码是用来进行白名单校验的,即如果输入的文字符合条件的话就返回true,否则返回false。
1 private static final String[] ENABLED_TEXTS = new String[]{"a", "b", "c", "d", "e"}; 2 3 public boolean isEnabled(String input) { 4 for (String text : ENABLED_TEXTS) { 5 if (text.equals(input)) { 6 return true; 7 } 8 } 9 return false; 10 }
当改成使用正则表达式进行校验的时候,代码变成下面这样:
1 public boolean isEnabled(String input) { 2 return input.matches("a|b|c|d|e"); 3 }
4.用异常代替错误号
有一段报名课程的代码:
1 public String register(int userId, int lessonId) { 2 int ret = checkUser(userId); 3 if (ret != 0) { 4 return "User does not exist."; 5 } 6 7 ret = checkLesson(lessonId); 8 if (ret != 0) { 9 return "Lesson does not exist."; 10 } 11 12 ret = checkLessonExpire(lessonId); 13 if (ret != 0) { 14 return "Lesson is expired."; 15 } 16 17 ret = register(userId, lessonId); 18 if (ret != 0) { 19 return "Register failed."; 20 } 21 22 return ""; 23 }
其中有若干处用到了ret这个变量,ret的值检查代码使得代码变长,而且业务流程代码距离拉开了,变得不容易理解。
1 public void register(int userId, int lessonId) throws BusinessException { 2 checkUser(userId); 3 checkLesson(lessonId); 4 checkExpire(lessonId); 5 registerLesson(userId, lessonId); 6 }
当把上述方法原来的int类型返回值去掉,改成throws BusinessException。修改之后的代码变得紧凑多了。
5.位运算
计算机表示数据是采用二进制的,几乎所有的计算机培训课程都从二进制开始,但是当写程序到一定程度之后,却忘记了利用二进制的基本运算来简化代码,加快效率。
a.奇偶运算
思路1:常见做法是数字除以2求余数
1 public class OddEven { 2 3 public boolean isEven(int num) { 4 return num % 2 == 0; 5 } 6 7 public static void main(String[] args) { 8 OddEven instance = new OddEven(); 9 System.out.println("3 is " + (instance.isEven(3)?"Even":"Odd")); 10 } 11 }
思路2:采用位运算之后,代码变成下面这样:
1 public class OddEven { 2 3 public boolean isEven(int num) { 4 return (num & 1) == 0; 5 } 6 7 public static void main(String[] args) { 8 OddEven instance = new OddEven(); 9 System.out.println("3 is " + (instance.isEven(3)?"Even":"Odd")); 10 System.out.println("36 is " + (instance.isEven(3)?"Even":"Odd")); 11 } 12 }
进一步,我们来看一个斑马线的例子。
思路1的做法:
1 import java.awt.Color; 2 3 public class Zembra { 4 5 public Color getZembraColor(int index) { 6 if (index % 2 ==0) { 7 return Color.GRAY; 8 } else { 9 return Color.WHITE; 10 } 11 } 12 13 public static void main(String[] args) { 14 Zembra zembra = new Zembra(); 15 System.out.println(zembra.getZembraColor(23).toString()); 16 } 17 }
思路2的做法:
1 import java.awt.Color; 2 3 public class Zembra { 4 5 public Color getZembraColor(int index) { 6 Color[] colors = new Color[]{Color.GRAY, Color.WHITE}; 7 return colors[index & 1]; 8 } 9 10 public static void main(String[] args) { 11 Zembra zembra = new Zembra(); 12 System.out.println(zembra.getZembraColor(23).toString()); 13 } 14 }
代码行数比思路1短了,并且,代码的执行效率提高了。但是易懂性有所降低。
b. Bit Mask
有些时候我们需要有多种不同层级的属性来表征事物的属性,或者对于同一事物的多种可以并存的属性。
比如:字体的下划线、粗体、斜体。
如果采用若干个变量来定义各种属性则每种属性都要设立一个变量。随着可以扩展的属性越来越多,所需要的变量也越来越多,将降低代码的可读性,可理解性。
为了防止这种属性扩大引起的变量增多的麻烦,以及为未来可能的扩展留有余地,可以采用Bit Mask。
比如:字体来说每一位(bit)表示一种属性。例如从最后一位开始向左,依次为:下划线、粗体、斜体、删除线、双删除线、上标、下标...
那么想要知道某一位是否被设置可以通过位运算来获取。
例如:Font.style来定义那么,计算是否为粗体,那么下列算式用来计算。
Font.style & 2 == 2
同样,该技术被应用于窗口属性、文本框语种限制等处。
c. 四则运算
当需要计算一个中间值的时候,采用的是(a + b) / 2的形式,但是这个算法有一个Bug,当a+b > Integer.Max的时候,会将计算结果变成负数。
正确的形式应该是 (a + b) >>> 1;
具体可以参照Java的二分法Bug报告: