好久没有写技术性文章了。最近趁着工作不是特别忙,再一次拿出一些时间研究Rxjava。在研究的过程中,我发现自己进展比较慢,在回过头来反省自己的时候,发现我可能连一些基本的问题还没有搞清楚就开始研究RXJAVA,相当于地基还没有打好,就开始修建高楼大厦。这些基本的问题里有一个最核心的问题:我们为什么要用Rxjava?
关于这个问题,不用说我,相信很多现在正在使用rxjava的人可能都不一定想的很透彻,大多都是人云亦云,随波逐流。李笑来说过:我们在这个世界上最宝贵的财富--注意力容易掉进三个坑,而随波逐流就是这其中一个坑。所以为了不让自己掉进这么一个坑,也打算在继续研究Rxjava前,先解决这个问题:我们为什么要用rxjava.
首先要先理清这么一个问题:Rxjava和我们平时写的程序有什么不同。相信稍微对Rxjava有点认知的朋友都会深深感受到用这种方式写的程序和我们一般写的程序有很明显的不同。我们一般写的程序 统称为命令式程序,是以流程为核心的,每一行代码实际上都是机器实际上要执行的指令。而Rxjava这样的编程风格,称为函数响应式编程。函数响应式编程是以数据流为核心,处理数据的输入,处理以及输出的。这种思路写出来的代码就会跟机器实际执行的指令大相径庭。所以对于已经习惯命令式编程的我们来说,刚开始接触Rxjava的时候必然会很不适应,而且也不太符合我们平时的思维习惯。
那么问题来了,既然函数响应式编程如此的不符合我们的惯性思维,那么为什么我们越来越多的人开始尝试运用rxJava以及函数响应式编程思想来写程序了呢?
不如我们直接从实例来入手好了。我们设想这么一个简单的实例,一个小的图书管理应用,我现在要做的是根据图书的ID号从服务器取得该图书的信息,并且做了修改再提交回去。很简单的一个业务逻辑,主要是和大家一起体验思维过程。
首先我们应该先写出书籍这个对象的实体类:
public class Book{
public int id;
public String name;
public String author;
}
然后我们再来写对书籍进行网络调用的接口:
public interface IBookService{
public Book getBookById(int bookId);
public void updateBook(Book book);
}
相信这个接口大家并不难理解,一个是根据id从服务器得到该Book对象,另一个方法就是提交修改后的book 对象到服务器,具体实现在这里就不详细说明了哈,在这里就假设有个类DefaultBookService实现了该接口。那么我们开始实现业务层的逻辑:
public class BookBusiness{
private IBookService mService;
public BookBusiness(){
mService = new DefaultBookService();
}
public void updateBook(int bookId){
Book book = mService.getBookById(bookId);
if(book!=null){
//book.setAuthor("小7");
book.aughor = "小7";
mService.updateBook(book);
}
}
}
这个方法好像也并不难理解,也不复杂。那么我们不就可以不需要用什么函数响应式编程就可以了吗?
先别急,我们看看上面的代码有没有问题呢?对了,我们上面的代码一直是在用主线程来进行网络请求调用!而在我们实际的项目中,网络请求是一定要异步线程调用的,这样才不会出现线程阻塞的问题。所以我们需要修改一下代码:
public interface Callback<T>{
public void onResult(T result);
public void onError(Exception exception);
}
public interface IBookService{
public void getBookById(int bookId,Callback<Book> callback);
public void updateBook(Book book ,Callback<void> callback);
}
这段代码增加了一个回调接口以便在服务器请求后通过回调方法把结果回传给业务层。
public class BookBusiness{
private IBookService mService;
public BookBusiness(){
mService = new DefaultBookService();
}
public void updateBook(int bookId,Callback<Void> callback){
mService.getBookById(1,new Callback<Book>{
public void onResult(Book result){
if(result!=null){
result.author="小7";
mService.updateBook(result,callback);
}
}
public void onError(Exception exception){
callback.onError(exception);
}
});
}
}
这样在我们的业务代码里面就会有匿名类,可读性自然就下降。我们这个时候可以想想办法,看如何消除掉匿名类。这个时候我们可以考虑在请求调用的方法里仅仅返回一个临时对象来封装实际的网络请求。
public class Task<T>{
public void doHandle(Callback<T> callback);
}
这个时候我们需要在IBookService上面封装一层:
public class BookServiceWrapper{
private IBookService mService;
public BookServiceWrapper(){
mService = new DefaultBookService();
}
public Task<Book> getBookById(int bookId){
return new Task<Book>(){
public void doHandle(Callback<Book> callback){
mService. getBookById(bookId,callback);
}
};
}
public Task<Void> updateBook(final Book book){
return new Task<Void>{
public void doHandle(final Callback<Void> callback){
mService.updateBook(book,callback);
}
};
}
}
这样业务层的代码由原来的直接对接口的引用转为对这个封装的类的引用:
public class BookBusiness{
private BookServiceWrapper mWrapper;
public BookBusiness(){
mWrapper = new BookServiceWrapper();
}
public Task<Void> updateBook(int bookId){
return new Task<Void>(){
public void doHandle(final Callback<Void> callback){
mWrapper.getBookById.doHandle(new Callback<Book>(){
public void onResult(Book result){
if(result!=null){
result.setAuthor("小7");
mWrapper.updateBook(result).doHandle(callback);
}
}
public void onError(Exception exception){
callback.onError(exception);
}
} );
}
}
}
}
这样做好像并没有明显提高代码的可读性。那么我们开始继续优化。其实我们看代码里复杂的地方就是嵌套了不少Callback回调类。那么我们可以想想是否可以把callback分离开来呢?我们可以先定义一个代表函数的接口。
public interface Func<T,R>{
public R call(T parameter);
}
有了这个接口定义后,我们可以实现一个Task抽象类来把这个代表函数的接口用上。
public abstract class AbstractTask<T> implements Task<T>{
public abstract void do Hanlde(Callback<T> callback);
portected <R> AbstractTask<R> map(Func<T,AbstractTask<R>> func){
AbstractTask<T> origin = this;
return new AbstractTask<R>{
public void doHandle(final Callback<R> rCallback){
origin.doHandle(new Callback<T>(){
public void onResult(T result){
AbstractTask<R> rTask = func.call(result);
rTask.doHandle(callback);
}
public void onError(Exception exception){
rCallback.onError(exception);
}
});
} } } }
看完这个实现,是不是思路逐渐明朗起来,我们在业务层里不需要再处理callback了:
public class BookBusiness{
private BookServiceWrapper mWrapper;
public BookBusiness(){
mWrapper = new BookServiceWrapper();
}
public AbstractTask<Void> updateBook(int bookId){
AbstractTask<Book> bookTask = mWrapper.getBookById(bookId);
AbstractTask<Void> baseTask = bookTask.map(new Func<Book,AbstractTask<Void>>(){
public AbstractTask<Void> call(Book book){
bookTask.updateBook(book);
}
});
}
}
}
}
现在,我们的推导完成了。大家有没有眼前一亮的感觉,当前的这段代码非常优雅的分离了业务代码和callback回调代码。而我们的AbstractTask类相当于Rxjava里面的Observable,Callback类相当于Observer。我这么说完,你是否看出了Rxjava的影子了呢。是不是很好的解决了我们的代码复杂度的问题了呢?
以上就是我一步步把我们的传统的编程思维转换为函数式相应编程的思维实现了自己的rxjava框架,当然rxjava框架远远不仅仅是这么点,但是我也希望通过自己的这点解释也能让大家真正开始明白我们为什么要使用函数式响应编程思想,这样采才能更加深入掌握它,也希望大家能多多交流,一起成长。