好久沒有寫技術性文章了。最近趁著工作不是特別忙跛十,再一次拿出一些時間研究Rxjava彤路。在研究的過程中,我發(fā)現(xiàn)自己進展比較慢芥映,在回過頭來反省自己的時候洲尊,發(fā)現(xiàn)我可能連一些基本的問題還沒有搞清楚就開始研究RXJAVA,相當于地基還沒有打好奈偏,就開始修建高樓大廈坞嘀。這些基本的問題里有一個最核心的問題:我們?yōu)槭裁匆肦xjava?
關于這個問題,不用說我惊来,相信很多現(xiàn)在正在使用rxjava的人可能都不一定想的很透徹丽涩,大多都是人云亦云,隨波逐流裁蚁。李笑來說過:我們在這個世界上最寶貴的財富--注意力容易掉進三個坑矢渊,而隨波逐流就是這其中一個坑。所以為了不讓自己掉進這么一個坑枉证,也打算在繼續(xù)研究Rxjava前昆淡,先解決這個問題:我們?yōu)槭裁匆胷xjava.
首先要先理清這么一個問題:Rxjava和我們平時寫的程序有什么不同。相信稍微對Rxjava有點認知的朋友都會深深感受到用這種方式寫的程序和我們一般寫的程序有很明顯的不同刽严。我們一般寫的程序 統(tǒng)稱為命令式程序昂灵,是以流程為核心的,每一行代碼實際上都是機器實際上要執(zhí)行的指令舞萄。而Rxjava這樣的編程風格眨补,稱為函數(shù)響應式編程。函數(shù)響應式編程是以數(shù)據(jù)流為核心倒脓,處理數(shù)據(jù)的輸入撑螺,處理以及輸出的。這種思路寫出來的代碼就會跟機器實際執(zhí)行的指令大相徑庭崎弃。所以對于已經(jīng)習慣命令式編程的我們來說甘晤,剛開始接觸Rxjava的時候必然會很不適應,而且也不太符合我們平時的思維習慣饲做。
那么問題來了线婚,既然函數(shù)響應式編程如此的不符合我們的慣性思維,那么為什么我們越來越多的人開始嘗試運用rxJava以及函數(shù)響應式編程思想來寫程序了呢盆均?
不如我們直接從實例來入手好了塞弊。我們設想這么一個簡單的實例,一個小的圖書管理應用,我現(xiàn)在要做的是根據(jù)圖書的ID號從服務器取得該圖書的信息游沿,并且做了修改再提交回去饰抒。很簡單的一個業(yè)務邏輯,主要是和大家一起體驗思維過程诀黍。
首先我們應該先寫出書籍這個對象的實體類:
public class Book{
public int id;
public String name;
public String author;
}
然后我們再來寫對書籍進行網(wǎng)絡調用的接口:
public interface IBookService{
public Book getBookById(int bookId);
public void updateBook(Book book);
}
相信這個接口大家并不難理解袋坑,一個是根據(jù)id從服務器得到該Book對象,另一個方法就是提交修改后的book 對象到服務器眯勾,具體實現(xiàn)在這里就不詳細說明了哈咒彤,在這里就假設有個類DefaultBookService實現(xiàn)了該接口。那么我們開始實現(xiàn)業(yè)務層的邏輯:
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);
}
}
}
這個方法好像也并不難理解镶柱,也不復雜。那么我們不就可以不需要用什么函數(shù)響應式編程就可以了嗎模叙?
先別急歇拆,我們看看上面的代碼有沒有問題呢?對了范咨,我們上面的代碼一直是在用主線程來進行網(wǎng)絡請求調用故觅!而在我們實際的項目中,網(wǎng)絡請求是一定要異步線程調用的渠啊,這樣才不會出現(xiàn)線程阻塞的問題输吏。所以我們需要修改一下代碼:
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);
}
這段代碼增加了一個回調接口以便在服務器請求后通過回調方法把結果回傳給業(yè)務層。
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);
}
});
}
}
這樣在我們的業(yè)務代碼里面就會有匿名類替蛉,可讀性自然就下降贯溅。我們這個時候可以想想辦法,看如何消除掉匿名類躲查。這個時候我們可以考慮在請求調用的方法里僅僅返回一個臨時對象來封裝實際的網(wǎng)絡請求它浅。
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);
}
};
}
}
這樣業(yè)務層的代碼由原來的直接對接口的引用轉為對這個封裝的類的引用:
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);
}
} );
}
}
}
}
這樣做好像并沒有明顯提高代碼的可讀性。那么我們開始繼續(xù)優(yōu)化镣煮。其實我們看代碼里復雜的地方就是嵌套了不少Callback回調類姐霍。那么我們可以想想是否可以把callback分離開來呢?我們可以先定義一個代表函數(shù)的接口典唇。
public interface Func<T,R>{
public R call(T parameter);
}
有了這個接口定義后镊折,我們可以實現(xiàn)一個Task抽象類來把這個代表函數(shù)的接口用上。
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);
}
});
} } } }
看完這個實現(xiàn)介衔,是不是思路逐漸明朗起來恨胚,我們在業(yè)務層里不需要再處理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);
}
});
}
}
}
}
現(xiàn)在,我們的推導完成了夜牡。大家有沒有眼前一亮的感覺与纽,當前的這段代碼非常優(yōu)雅的分離了業(yè)務代碼和callback回調代碼。而我們的AbstractTask類相當于Rxjava里面的Observable,Callback類相當于Observer塘装。我這么說完急迂,你是否看出了Rxjava的影子了呢。是不是很好的解決了我們的代碼復雜度的問題了呢蹦肴?
以上就是我一步步把我們的傳統(tǒng)的編程思維轉換為函數(shù)式相應編程的思維實現(xiàn)了自己的rxjava框架僚碎,當然rxjava框架遠遠不僅僅是這么點,但是我也希望通過自己的這點解釋也能讓大家真正開始明白我們?yōu)槭裁匆褂煤瘮?shù)式響應編程思想阴幌,這樣采才能更加深入掌握它勺阐,也希望大家能多多交流,一起成長矛双。