空对象模式 (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哈哈~