教你寫響應式框架(二)
還要做什麼?
在教你寫響應式框架(一)中我們介紹了觀察者模式,現在我們將基於上一篇中的程式碼進行改造。當然,我們是有目的的改造:
- 在響應式框架中,觀察者是可能隨時產生,種類多,生命週期卻短暫.
- 我們希望操作是非同步的,並且只有在觀察者被註冊到被觀察者上時,被觀察者才生效.
在開始改造之前,為了避免沒看過上一篇的童鞋產生斷裂感,我仍然先貼一下觀察者的實現,為了從能夠”望文生義”,其中有一些小細節我做了調整:
//被觀察者
public abstract class Observable {
private List<Observer> list = new ArrayList<>();
public void attach(Observer observer) {
list.add(observer);
}
public void detach(Observer observer) {
list.remove(observer);
}
public void notifyObservers(String newState) {
list.stream().forEach(ob->{ob.update(newState);});
}
}
//被觀察者的具體實現
public class ConcreteObservable extends Observable {
public void change(String state) {
this.notifyObservers(state);
}
}
//觀察者介面
public interface Observer {
void update(String state);
}
//觀察者的具體實現
public class ConcreteObserver implements Observer {
@Override
public void update (String state) {
System.out.println("主題狀態改變了:" + state);
}
}
//客戶端測試程式碼
public class Client {
public static void main(String[] args) {
ConcreteObservable subject = new ConcreteObservable();
subject.attach(new Observer() {
@Override
public void update(String state) {
System.out.println("主題狀態變化:" + state);
}
});
subject.change("2");
}
}
別樣的觀察者模式
針對上面提到的第一個需求,我們很容易理解,只提供給使用者相關的介面就行,具體實現由使用者根據實際情況來實現.
那如何實現第二個需求呢?暫時我們先不說,直接從程式碼中看實現:
//被觀察者
public class Observable {
protected OnAttach onAttach;
private Observable() {
}
public Observable(OnAttach onAttach) {
this.onAttach = onAttach;
}
public void attach(Observer observer) {
onAttach.call(observer);
}
public interface OnAttach {
void call(Observer observer);
}
}
//觀察者
public interface Observer {
void update(String state);
}
//客戶端程式碼
public class Client {
public static void main(String[] args) {
Observable observable = new Observable(new Observable.OnAttach() {
@Override
public void call(Observer observer) {
observer.update("1");
}
});
observable.attach(new Observer() {
@Override
public void update(String state) {
System.out.println("state:" + state);
}
});
}
}
經過一番折磨之後,對原有的程式碼進行了重構,重點關注Observable類.在Observable中,我們定義OnAttach介面,該介面負責通知觀察者.同時attach方法在將觀察者註冊到被觀察者上之後,會呼叫OnAttach的call方法來實現自動通知,這樣做的好處就是我們不需要再手動呼叫call方法來通知被觀察者了—-對使用者遮蔽細節.
為了更方便理解,我們將客戶端的程式碼寫成你所熟悉的方式:
public class Client {
public static void main(String[] args) {
//註冊關係,簡化了手動通知觀察者的過程
Observable.OnAttach onAttach = new Observable.OnAttach() {
@Override
public void call(Observer observer) {
observer.update("1");
}
};
//被觀察者
Observable observable = new Observable(onAttach);
//觀察者
Observer observer = new Observer() {
@Override
public void update(String state) {
System.out.println("state:" + state);
}
};
//將觀察者註冊到被觀察者上
observable.attach(observer);
}
}
我們發現最大的變化就是被觀察者,尤其是通過OnAttach介面來實現自動通知,現在我們分析一下:
首先,我們實現OnAttach介面,該介面含有唯一的方法call.call方法接受一個Observer物件,此物件就是我們的觀察者物件observer.當執行observable.attach(observer)方法時,會引起call方法的呼叫,進而執行observer.update(“1”),然後執行System.out.println(“state:” + state);,不難發現這就是標準觀察者模式的變種.
重構,改善原有設計
分析完之後,我們再看一下程式碼,哇,好low,這麼低階的程式碼看起來像坨大便一樣.既然要作為一個框架,必然要儘可能的通用和易於理解.因此我們在上面程式碼的基礎上修改兩點:
- 引入泛型
- 用靜態工廠方法代替構造器.
引入泛型想必每個人都能理解,但是用靜態工廠可能有些人覺得多此一舉了,對此請各位自行查閱Joshua Bloch大神的<>的第一節吧.
重構後的程式碼:
//被觀察者
public class Observable<T> {
protected OnAttach onAttach;
public Observable(OnAttach onAttach) {
this.onAttach = onAttach;
}
public static <T> Observable<T> create(OnAttach<T> onAttach) {
return new Observable(onAttach);
}
public void attach(Observer<T> observer) {
onAttach.call(observer);
}
public interface OnAttach<T> {
void call(Observer<? super T> observer);
}
}
//觀察者
public interface Observer<T> {
void update(T state);
}
//客戶端程式碼
public class Client {
public static void main(String[] args) {
//註冊關係,簡化了手動通知觀察者的過程
Observable.OnAttach onAttach = new Observable.OnAttach() {
@Override
public void call(Observer observer) {
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
observer.update(list);
}
};
//被觀察者
Observable observable = Observable.create(onAttach);
//觀察者
Observer observer = new Observer<ArrayList<Integer>>() {
@Override
public void update(ArrayList<Integer> state) {
state.stream().forEach(p -> {
System.out.println(p);
});
}
};
//將觀察者註冊到被觀察者上
observable.attach(observer);
}
}
現在的程式碼看起來是不是好一點了?到現在,你可能會說:壓根沒什麼用嘛.對的,起碼到目前來說,並沒有什麼用.在下一節中,我們將引入操作符,這才是我們關注的重點.