空对象模式 (null object Pattern)是一种软件设计模式。可以用于返回无意义的对象时,它可以承担处理null的责任。有时候空对象也被视为一种设计模式。
在写代码的时候我们经常会遇到空指针,为了避免空指针的发生需要做一些判断。如果是复杂对象的话,还需要一层层地去判断。这个时候我就无比怀念groovy、kotlin这类语言。可以使用形如:
user?.address?.name
这样的语法糖,而无需一层层的判断。
google的guava库提供了Optional类,可以有效的判断null对象。
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
guava可以创建指定的null对象
Optional<Integer> nullable=Optional.fromNullable(null);
System.out.println("from Nullable Optional isPresent:"+nullable.isPresent()); //returns from Nullable Optional isPresent:false
在java 8中也新增了Optional类。
同时,我自己也仿照guava的Optional类写了一个简化版的Optional并附上使用方法,它借助了rxjava。
import rx.Observable;
/**
* 使用方法:
* String s = null;
* Optional.ofNullable(s).orElse("default")); // 如果s为null,则显示default,否则显示s的值
* @author Tony Shen
*
*/
public class Optional<T> {
Observable<T> obs;
public Optional(Observable<T> obs) {
this.obs = obs;
}
public static <T> Optional<T> of(T value) {
if (value == null) {
throw new NullPointerException();
} else {
return new Optional<T>(Observable.just(value));
}
}
public static <T> Optional<T> ofNullable(T value) {
if (value == null) {
return new Optional<T>(Observable.<T>empty());
} else {
return new Optional<T>(Observable.just(value));
}
}
public T get() {
return obs.toBlocking().single();
}
public T orElse(T defaultValue) {
return obs.defaultIfEmpty(defaultValue).toBlocking().single();
}
}
上面讲了那么多,是为了防止空指针出现,现在我们来看看空对象模式具体的使用场景吧,假设我们在代码中使用了链式调用,形如:
client = RestClient.post(request.getUrl())
.readTimeout(request.getReadTimeoutMillis())
.connectTimeout(request.getConnectTimeoutMillis())
.addHeaders(request.getHeaders());
只要某一个环节出现问题,就会有空指针的隐患的。比如RestClient的post()方法内部出现了问题,那就非常严重了,后面整个链路都会断掉,报空指针异常。
那我们如何保证整个链路不断呢?在post()方法里面会调用一个getConnection()方法
public HttpURLConnection getConnection() {
if (connection == null)
connection = createConnection();
if (connection == null) {
clientIsNull = true;
connection = NullConnection.createNull(url);
}
return connection;
}
请注意,在getConnection()方法里会有两个connection == null的判断。理论上,第一次调用createConnection()方法时,connection是不会为空的。但是在使用某个APM sdk时,确实发现有极少的概率connection会为空。如果它为空,那么我们在第二个判断中增加了如下的代码,来保证返回的connection不为null,提高程序的健壮性。
connection = NullConnection.createNull(url);
我们来看看,NullConnection是怎么回事
/**
* Created by Tony Shen on 2016/12/6.
*/
public class NullConnection extends HttpURLConnection {
private NullConnection(URL url) {
super(url);
}
@Override
public void disconnect() {
}
@Override
public boolean usingProxy() {
return false;
}
@Override
public void connect() throws IOException {
}
public boolean isNull() {
return true;
}
public static NullConnection createNull(URL url) {
return new NullConnection(url);
}
}
其实,基本没做啥事,主要多了一个createNull()的方法。它会产生一个NullConnection对象,它的作用是防止产生null,从而保证原先的RestClient链路是正常的。这就是空对象模式。
链式调用,会让代码更加清晰,带给我们的好处是非常明显的。只要我们处理好空指针的隐患,就可以更愉悦地写代码了O(∩_∩)O哈哈~