1. 程式人生 > >教你寫響應式框架(二)

教你寫響應式框架(二)

還要做什麼?

教你寫響應式框架(一)中我們介紹了觀察者模式,現在我們將基於上一篇中的程式碼進行改造。當然,我們是有目的的改造:

  1. 在響應式框架中,觀察者是可能隨時產生,種類多,生命週期卻短暫.
  2. 我們希望操作是非同步的,並且只有在觀察者被註冊到被觀察者上時,被觀察者才生效.

在開始改造之前,為了避免沒看過上一篇的童鞋產生斷裂感,我仍然先貼一下觀察者的實現,為了從能夠”望文生義”,其中有一些小細節我做了調整:

//被觀察者
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);

    }
}

現在的程式碼看起來是不是好一點了?到現在,你可能會說:壓根沒什麼用嘛.對的,起碼到目前來說,並沒有什麼用.在下一節中,我們將引入操作符,這才是我們關注的重點.