Why ?
合法性检查对于程序的健壮性具有重要作用。在Android开发中,良好的合法性检查设计机制可以使程序更加清晰,产生bug更少,交互更加友好。
What ?
合法性检查的目的在于确定边界。对于在程序中流动的数据来说,其流动路径往往是很复杂的。为了保证程序的健壮性,需要保证数据从一端流入并且从另外一端流出的过程中不会发生异常行为。数据流经的每一个方法都必须保证异常被正确处理。为了让每一个函数清晰简单,有必要在数据进入函数之前保证其合法性。让函数中的逻辑不受数据边界的影响。
Who ?
接受合法性检查的对象一般是某一事件触发后需要的初始数据,大多以函数参数形式进行调用。对于Android开发来说。最频繁需要进行检查的莫过于EditText组件。该组件接受用户的输入,是很多功能的数据来源。网络请求的返回数据也是很重要的数据来源。正确处理这些初始的数据,保证其合法性,使得之后的程序逻辑不再受数据异常的影响,同时给予用户一个良好的交互反馈是必须要做的。
When
合法性检查发生在数据中转的入口(也可能是运行的开始)。
How
合法性检查需要从以下几方面着手
- 安全的类型转换工厂
由于程序的数据来源是很复杂的,数据往往需要进行加工和各种数据转换。虽然JAVA提供了许多数据转换的方法。但是,直接在功能代码中使用这些方法是不明智的。很可能产生异常,而当程序没有适当的捕捉和处理异常时,就有可能发生程序崩溃。安全的做法是建立一个安全的类型转换工厂,统一处理需要进行类型转换的数据。
举例:
假设用户输入两个数字,程序需要将两个数字加起来。直接进行类型转换是不安全的。
bad practice :
Double plus(String inputA, String inputB) {
return Double.valueOf(inputA) + Double.valueOf(inputB);
}
good practice:
private Integer plus(String inputA, String inputB){
Integer convertA = StringConverter.getInteger(inputA) ;
Integer convertB = StringConverter.getInteger(inputB) ;
if(convertA != null && convertB != null) {
return convertA + convertB;
}else {
//null must be handled to avoid NullPointerException
return null;
}
}
public class StringConverter {
public static Integer getIntegerValue(String var) {
try{
return Integer.valueOf(var);
}catch(Exception e) {
//ignore much code of print exception and other options
return null;
}
}
}
- 安全的构造函数
在构造函数中检查初始化参数是一个很好的习惯,可以防止不恰当的初始化发生。
举例:
public class Age {
int age;
public Age(int age) throws AgeNegativeException {
if(age == null || age < 0) {
//age can never be a negative number or null
throw(new AgeNegativeException());
}else {
this.age = age;
}
public int getAge() { return age;}
}
public class AgeNegativeException extends Exception {
//exception definition details ignored
}
- 分层清晰的异常处理机制
异常有可能发生在入口处,也有可能发生在运行时。数据流经的地方,都需要确定自己的边界。每一个函数都应该明确自己的责任。对于整个程序结构来说。针对各个不同的模块需要定义相关的异常并且在相应的层面上去处理异常。
举例:
结合上面两个例子,用户需要做一个年龄累加统计。每次输入两个需要累加的年龄,返回累加后的年龄。
public Age addAge(String inputA, String inputB) throws AgeNegativeException {
//calculate and return
return new Age(plus(inputA, inputB));
}
example1:
inputA = “dhkja”; inputB = "10";
如果用户输入了这两个值。那么
plus(inputA, inputB)
会返回一个null。在构造Age的时候就会抛出异常。由于这个异常是用户输入不当造成的因此需要将异常再次抛出到Activity那一层,让Activity提示用户输入有误。
- 为每一个文本框设置校验
为文本框设置校验涉及到两方面
1、校验时间
2、校验提示
校验时间:
- 用户输入前
- 用户输入时
- 失去焦点时
- 用户触发相关方法时
前三种可以通过设置文本监听进行处理,最后一种属于事后检验不设置文本监听。
校验提示:
- 弹出框形式
- ErrorMessage
弹出框可以弹出Toast或者一个dialog,ErrorMessage可以通过设置EditText 的ErrorMessage实现。也可以借助开源组件实现。例如android-edittext-validator
经过如此设计,就不太会受到数据异常的困扰了。关于文本监听和校验提示,会在另外文章说明。