高手眼中的觀察者模式和新手有什麼不一樣

觀察者模式,是使用很多的一種模式,初次瞭解,只是根據入門demo寫個例子,但是不知道用在哪,怎麼用,很教科書。
個人很喜歡比較實戰的的博客或者資料。
最近又惡補了一把,感覺有點小收穫,記錄下。

基礎部分

一、觀察者模式的基本經典結構

在這裏插入圖片描述
在這裏插入圖片描述

二、入門小demo

經典小demo1:

在這裏插入圖片描述
Observer

/**
 * 觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象
 */
public interface Observer {
    /**
     * 更新的接口
     */
    public void update(Subject subject);
}

ConcreteObserver

//具體觀察者對象,實現更新的方法,使用自身的狀態和
public class ConcreteObserver implements Observer {

    @Override
    public void update(Subject subject) {
        //具體的實現
        //這裏可能需要更新觀察者的狀態,使其與目標的狀態保持一致
        String message = ((ConcreteSubject) subject).getSubjectState();
        System.out.println("收到一通知: 獲取到的狀態是: " + message);
    }

}

Subject

/**
 * 目標對象,它知道觀察它的觀察者,並提供註冊和刪除觀察者的接口
 */
public class Subject {
    /**
     * 用來保存註冊的觀察者對象
     */
    private List<Observer> observers = new ArrayList<>();
    /**
     * 註冊觀察者對象
     */
    public void attach(Observer observer){
        observers.add(observer);
    }
    /**
     * 刪除觀察者對象
     */
    public void detach(Observer observer){
        observers.remove(observer);
    }
    /**
     * 通知所有註冊的觀察者對象
     */
    protected void notifyObservers(){
        for (Observer observer: observers){
            observer.update(this);
        }
    }

}

ConcreteSubject

/**
 * 具體的目標對象,負責吧有關狀態存入到相應的觀察者對象
 * 並在自己狀態
 */
public class ConcreteSubject extends Subject {
    /**
     * 目標對象的狀態
     */
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
        this.notifyObservers();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //觀察者
        Observer concreteObserver1 = new ConcreteObserver();
        Observer concreteObserver2 = new ConcreteObserver();
        //目標對象 即被觀察者 目標可以有多個,此demo通過 state區分
        ConcreteSubject subject1 = new ConcreteSubject();
        //註冊觀察者
        subject1.attach(concreteObserver1);
        subject1.attach(concreteObserver2);
//        ConcreteSubject subject2 = new ConcreteSubject();
//        //註冊觀察者
//        subject2.attach(concreteObserver1);
//        subject2.attach(concreteObserver2);
        //發出通知
        subject1.setSubjectState("通知1:已經下發了");
//        System.out.println("===換一個主題======");
//        subject2.setSubjectState("通知2:已經下發了");

    }
}

運行結果: 主題發一個消息,觀察者都能收到:
在這裏插入圖片描述

大話設計模式中看門放哨小案例

在這裏插入圖片描述
Subject

public interface Subject {
    
	/**
	 * 添加觀察者
	 * @param observer
	 */
	void addObserver(Observer observer);
	/**
	 * 移除指定的觀察者
	 * @param observer
	 */
	void removeObserver(Observer observer);
	/**
	 * 移除所有的觀察者
	 */
	void removeAll();
	
	/**
	 * data 是要通知給觀察者的數據
	 * 因爲Object是所有類的父類,可以使用多態,當然 你也可以使用 泛型
	 * @param data
	 */
	void notifyAllObserver(Object data);
	
    /**
     * 單獨 通知某一個觀察者
     * @param observer
     * @param data
     *  data 是要通知給觀察者的數據
	 * 因爲Object是所有類的父類,可以使用多態,當然 你也可以使用 泛型
     */
	void notify(Observer observer,Object data);

}

ConcreteSubject


/**
 * 具體的主題對象 
 * 這裏就不實現線程安全的功能了,
 * 有興趣的話可以參考java.util報下的Observable
 * @author xujun
 *
 */

public class ConcreteSubject implements Subject {

	List<Observer> mList = new ArrayList<>();

	@Override
	public void addObserver(Observer observer) {
		// 確保相同的觀察者只含有一個
		if (observer == null) {
			throw new NullPointerException("observer == null");
		}
		if (!mList.contains(observer)) {
			mList.add(observer);
		}
	}

	@Override
	public void removeObserver(Observer observer) {
		mList.remove(observer);
	}

	@Override
	public void removeAll() {
       mList.clear();
	}

	@Override
	public void notifyAllObserver(Object data) {
		for (Observer observer : mList) {
			observer.update(data);
		}
	}

	@Override
	public void notify(Observer observer, Object data) {
		if(observer!=null){
			observer.update(data);
		}
	}

}

Observer

/**
 * 觀察者接口
 * @author Administrator
 *
 */
public interface Observer {
	
	/**
	 * 
	 * @param data    被觀察者傳遞給觀察者的 數據
	 */
	void update(Object data);
	

}

CartoonObserver

public class CartoonObserver implements Observer {

	@Override
	public void update(Object data) {
		System.out.println( " 我是"+this.getClass().
				getSimpleName()+",  "+data+"別看漫畫了");	
	}

}

NBAObserver

public class NBAObserver implements Observer {
	public class CartoonObserver implements Observer {

		@Override
		public void update(Object data) {
			System.out.println( " 我是"+this.getClass().getSimpleName()+",  "+data+"別看漫畫了");
		}
	}

    @Override
    public void update(Object data) {
        System.out.println(" 我是" + this.getClass().getSimpleName() + ",  " + data + "別看NBA了");
    }

}

TestObserver

public class TestObserver {

    public static void main(String[] args) {
        //主題
        ConcreteSubject concreteSubject = new ConcreteSubject();
        //觀察者
        CartoonObserver cartoonObserver = new CartoonObserver();
        NBAObserver nbaObserver = new NBAObserver();
        //添加觀察者
        concreteSubject.addObserver(cartoonObserver);
        concreteSubject.addObserver(nbaObserver);
       //發佈消息通知
        concreteSubject.notifyAllObserver("老師來了");
    }
}

運行結果: 只要放哨的一發通知,觀察者就收到了。
在這裏插入圖片描述

三、經典觀察者模式的兩種使用方式: 推和拉

觀察者模式使用時,其實分2個階段:

  • 準備階段,維護目標和觀察者關係的階段

  • 實際運行階段,也就是目標發生變化,引起觀察者做出發應的階段

這裏說的使用方式,針對的是實際運行階段。獲取目標確切數據發起者的問題。

  • 推模型
    目標對象主動向觀察者推送目標的詳細信息,不管觀察者是否需要,這些數據,是目標對象定義,相當於廣播給觀察者。剛纔的例子中,第2個就是推模型。
  • 拉模型
    目標對象在通知觀察者的時候,只是把自己的引用給觀察者,觀察者根據需要,使用引用獲取。剛纔的例子中,第一個例子就是拉模型。目標對象吧this傳遞給觀察者。

開發中如果數據確定,可以用推模型,如果觀察者要得到的數據不固定,建議用拉模型,更加靈活,擴展性強。總之,還是拉模型好。


新手一般只學到上面。下面進入入深入思考的部分:


高級部分(應用場景)

一、 如何讓觀察者區別對待

上面的demo,均是目前通知觀察者的時候全部都通知,根據不同的情況來讓不同的觀察者處理操作,如何設計呢?

思路:2種,
一種是目標可以全部通知,但是觀察者不做任何操作,
另一種就是在目標裏面進行判斷,直接不通知了,
這裏推薦第二種,可以統一邏輯控制,並進行觀察者的統一分派,有利於業務控制和今後的擴展。

來個例子: 水質污染,根據污染情況分別通知檢測員,預警人員,檢測部門領導。
代如下:
在這裏插入圖片描述

WaterQualitySubject - 水質監測的目標對象

/**
 * 定義水質監測的目標對象
 */
public abstract class WaterQualitySubject {
    /**
     * 用來保存註冊
     */
    protected List<WatcherObserver> observers = new ArrayList<>();
    /**
     * 註冊觀察者對象
     */
    public void attach(WatcherObserver observer){
        observers.add(observer);
    }
    /**
     * 刪除觀察者對象
     */
    public void detach(WatcherObserver observer){
        observers.remove(observer);
    }
    /**
     * 通知相應的觀察者對象
     */
    public abstract void notifyWathers();
    /**
     * 獲取水質污染的級別
     */
    public abstract int getPolluteLevel();

}

WaterQuality - 具體的水質監測對象

/**
 * 具體的水質監測對象
 */
public class WaterQuality extends WaterQualitySubject {
    /**
     * 污染的級別,0表示正常,1表示輕度污染,2表示中度污染,3表示高度污染
     */
    private int polluteLevel = 0;

    /**
     * 獲取水質污染的級別
     */
    @Override
    public int getPolluteLevel() {
        return polluteLevel;
    }

    public void setPolluteLevel(int polluteLevel) {
        this.polluteLevel = polluteLevel;
        this.notifyWathers();
    }

    /**
     * 通知相應的觀察者對象
     */
    @Override
    public void notifyWathers() {
        //循環所在註冊的觀察者
        for (WatcherObserver watcher: observers){
            //開始根據污染級別判斷是否需要通知,由這裏總控
            if (this.polluteLevel >=0 ){
                //通知監測員做記錄
                if (("監測人員").equals(watcher.getJob())){
                    watcher.update(this);
                }
            }
            if (this.polluteLevel >=1 ){
                //通知預警人員
                if (("預警人員").equals(watcher.getJob())){
                    watcher.update(this);
                }
            }
            if (this.polluteLevel >=2 ){
                //通知監測員部門領導
                if (("監測部門領導").equals(watcher.getJob())){
                    watcher.update(this);
                }
            }

        }
    }

}

WatcherObserver

public interface WatcherObserver {
    /**
     * 被通知的方法
     * @param subject
     */
    public void update(WaterQualitySubject subject);
    /**
     * 設置觀察人員的職務
     */
    public void setJob(String job);
    /**
     * 獲取觀察人員的職務
     */
    public String getJob();


}

Watcher

public class Watcher  implements WatcherObserver{
    private String job;

    @Override
    public void update(WaterQualitySubject subject) {
        //這裏採用的是拉的方式
        System.out.println(job+"獲取到通知,當前污染級別爲:" + subject.getPolluteLevel());
    }

    @Override
    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String getJob() {
        return this.job;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //創建水質主題對象
        WaterQuality subject = new WaterQuality();
        //創建幾個觀察者
        WatcherObserver watcher1 = new Watcher();
        watcher1.setJob("監測人員");
        WatcherObserver watcher2 = new Watcher();
        watcher2.setJob("預警人員");
        WatcherObserver watcher3 = new Watcher();
        watcher3.setJob("監測部門領導");
        //註冊觀察者
        subject.attach(watcher1);
        subject.attach(watcher2);
        subject.attach(watcher3);
        //填寫水質報告
        System.out.println("當水質爲正常的時候-----------");
        subject.setPolluteLevel(0);
        System.out.println("當水質爲輕度污染的時候-----------");
        subject.setPolluteLevel(1);
        System.out.println("當水質爲中度污染的時候-----------");
        subject.setPolluteLevel(2);
    }
}

運行結果:
在這裏插入圖片描述

可以看到根據預警級別通知了不同的觀察者。
主要邏輯在目標對象實現類上,在if的判斷上是比較取巧的寫法,不是if 。。else if … .,而是多個if。組合if,好好體會學習,以後借鑑使用下。

二、如何不依賴抽象觀察者,也能實現觀察者模式:

上面的例子,都是有個抽象觀察者的角色的,目標對象直接操作抽象觀察者。如果不想使用抽象觀察者,考慮的思路如下:

  • 1.新建抽象觀察者類這個事情,別人給提供,我們實現它就好了,這裏我們可以使用java自帶的觀察者模式,他已經幫我們自動提供了抽象觀察者,和抽象目標類,我們按照他的規則直接使用就可以了。
  • 這裏一會展示 使用java自帶的觀察者模式的例子:
  • 2.註冊觀察者和通知觀察者的工作交給一個第三方,解耦目標和觀察者。
    這種實現的方式很多,主要思路是反射委託

這裏一會展示4個例子:

  • 反射委託實現上面第2個例子,上課看nba和動漫,老師來了,做出不同反應的例子。
  • 比如:模仿swing組件實現原理的例子(這個例子很特別,觀察者可以觀察多個目標對象
  • spring中listener實現觀察者模式的例子
  • springboot使用Guava框架提供的eventBus,實現事件處理的例子。

1.使用java自帶的觀察者模式的例子

java提供抽象觀察者Observer,抽象目標對象Observable,通知觀察者的方法名必須是update。通知前必須調動setChange()方法,具體代碼如下:

具體目標類(被觀察者)

/**
 * Title: GPer
 * Description: JDK提供的一種觀察者的實現方式,被觀察者
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-03
 */
public class GPer extends Observable {
    private String name = "GPer生態圈";
    private static GPer gper = null;

    private GPer() {
    }

    public static GPer getInstance(){
        if(null == gper){
            gper = new GPer();
        }
        return gper;
    }

    public void publishQuestion(Question question){
        System.out.println(question.getUserName() + "在" + this.name + "上提交了一個問題。");
        setChanged();
        notifyObservers(question);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

來個輔助的類,和觀察者模式沒啥關係的業務類:Question

/**
 * Title: Question
 * Description: TODO
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-03
 */
public class Question {
    //提問者
    private String userName;
    //提問問題
    private String content;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

具體的觀察者類:Teacher

public class Teacher implements Observer {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        GPer gper = (GPer)o;
        Question question = (Question)arg;
        System.out.println("===============================");
        System.out.println(name + "老師,你好!\n" +
                "您收到了一個來自“" + gper.getName() + "”的提問,希望您解答,問題內容如下:\n" +
                question.getContent() + "\n" +
                "提問者:" + question.getUserName());
    }
}

測試類:

public class ObserverTest {
    public static void main(String[] args) {
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("Tom");
        Teacher mic = new Teacher("Mic");

        //這爲沒有@Tom老師
        Question question = new Question();
        question.setUserName("小明");
        question.setContent("觀察者設計模式適用於哪些場景?");
        gper.addObserver(tom);
        gper.addObserver(mic);
        gper.publishQuestion(question);
    }
}

運行結果:
在這裏插入圖片描述
查看結果,完美的展示了目標對象通知所有觀察者的實現。

2.反射委託實現上面第2個例子,學生根據老師到來,做出不同反應的例子。

具體代碼:
在這裏插入圖片描述


/**
 * 事件對象的封裝類
 *
 * @author Administrator
 */
public class Event {
    //要執行方法的對象   
    private Object object;
    //要執行的方法名稱   
    private String methodName;
    //要執行方法的參數   
    private Object[] params;
    //要執行方法的參數類型   
    private Class[] paramTypes;

    public Event() {

    }

    public Event(Object object, String methodName, Object... args) {
        this.object = object;
        this.methodName = methodName;
        this.params = args;
        contractParamTypes(this.params);
    }

    //根據參數數組生成參數類型數組   
    private void contractParamTypes(Object[] params) {
        this.paramTypes = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            this.paramTypes[i] = params[i].getClass();
        }
    }


    public Object getObject() {
        return object;
    }

    //這裏省略了若干get和set方法

    /**
     * 根據該對象的方法名,方法參數,利用反射機制,執行該方法
     *
     * @throws Exception
     */
    public void invoke() throws Exception {
        Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
        if (null == method) {
            return;
        }
        method.invoke(this.getObject(), this.getParams());
    }


    public void setObject(Object object) {
        this.object = object;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Object[] getParams() {
        return params;
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    public Class[] getParamTypes() {
        return paramTypes;
    }

    public void setParamTypes(Class[] paramTypes) {
        this.paramTypes = paramTypes;
    }
}

EventHandler


/**
 * Title: EventHandler
 * Description: 事件的 處理者
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-05
 */
public class EventHandler {
    //是用一個List
    private List<Event> objects;

    public EventHandler(){
        objects=new ArrayList<Event>();
    }
    //添加某個對象要執行的事件,及需要的參數
    public void addEvent(Object object,String methodName,Object...args){
        objects.add(new Event(object,methodName,args));
    }
    //通知所有的對象執行指定的事件
    public void notifyX() throws Exception{
        for(Event e : objects){
            e.invoke();
        }
    }
}

通知者的 抽象類Notifier

public abstract class Notifier {
    private EventHandler eventHandler = new EventHandler();

    public EventHandler getEventHandler() {
        return eventHandler;
    }

    public void setEventHandler(EventHandler eventHandler) {
        this.eventHandler = eventHandler;
    }

    /**
     * 增加需要幫忙 放哨 的 學生
     *
     * @param object     要執行方法的對象
     * @param methodName 執行方法 的方法名
     * @param args       執行方法的參數
     */
    public abstract void addListener(Object object, String methodName,  Object... args);

    /**
     * 告訴所有要幫忙放哨的學生:老師來了
     */
    public abstract void notifyX();
}

通知者 GoodNotifier

public class GoodNotifier extends Notifier {

    @Override
    public void addListener(Object object, String methodName, Object... args) {
        System.out.println("有新的同學委託盡職盡責的放哨人!");
        EventHandler handler = this.getEventHandler();
        handler.addEvent(object, methodName, args);
    }

    @Override
    public void notifyX() {
        System.out.println("盡職盡責的放哨人告訴所有需要幫忙的同學:老師來了");
        try{
            this.getEventHandler().notifyX();
        }catch(Exception e){
            e.printStackTrace();
        }
    }


WatchCartoonListener

/**
 * Title: WatchCartoonListener
 * Description:  具體監聽者(觀察者)
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-05
 */
public class WatchCartoonListener extends GoodNotifier {

    public WatchCartoonListener() {
        System.out.println("WatchCartoonListener 我正在看漫畫,開始時間:"+ LocalDateTime.now().toString());
    }

    public void stopPlayingGame(Date date){
        System.out.println("WatchCartoonListener  停止看漫畫了,結束時間:"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
    }
}

WatchingNBAListener

public class WatchingNBAListener extends GoodNotifier {
    public WatchingNBAListener() {
        System.out.println("WatchingNBAListener我正在看NBA,開始時間是: " + LocalDateTime.now().toString());
    }
    public void stopWatchingTV(Date date){
        System.out.println("WatchingNBAListener 快關閉NBA直播 , 結束時間是:" +  new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
    }
}

測試類:

public class Test {
    public static void main(String[] args) {
        //創建一個盡職盡責的放哨者
        Notifier goodNotifier = new GoodNotifier();

//創建一個玩遊戲的同學,開始玩遊戲
        WatchCartoonListener playingGameListener = new WatchCartoonListener();

//創建一個看電視的同學,開始看電視
        WatchingNBAListener watchingTVListener = new WatchingNBAListener();
//玩遊戲的同學告訴放哨的同學,老師來了告訴一下
        goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看電視的同學告訴放哨的同學,老師來了告訴一下
        goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
        try {
            //一點時間後
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
//老師出現,放哨的人通知所有要幫忙的同學:老師來了
        goodNotifier.notifyX();


    }
}

運行結果:

public class Test {
    public static void main(String[] args) {
        //創建一個盡職盡責的放哨者
        Notifier goodNotifier = new GoodNotifier();

//創建一個玩遊戲的同學,開始玩遊戲
        WatchCartoonListener playingGameListener = new WatchCartoonListener();

//創建一個看電視的同學,開始看電視
        WatchingNBAListener watchingTVListener = new WatchingNBAListener();
//玩遊戲的同學告訴放哨的同學,老師來了告訴一下
        goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看電視的同學告訴放哨的同學,老師來了告訴一下
        goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
        try {
            //一點時間後
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
//老師出現,放哨的人通知所有要幫忙的同學:老師來了
        goodNotifier.notifyX();


    }
}

運行結果:
在這裏插入圖片描述

事件委託機制 分析
1.放哨者完全不知道做遊戲者的存在,完全解耦。(當然,功勞歸功於Event和EventHandler,且這兩個類具有通用性)
2.老師來了後遊戲者停止遊戲回到座位,看NBA者停止看NBA,看漫畫這停止看漫畫,玩遊戲這停止玩遊戲。(一次通知,執行了不同類的不同方法)
3.擴展性很高,再來一個打籃球的學生就先寫個打籃球學生類,並在測試代碼中告訴放哨者一下就好,放哨者完全沒有變。重用性好

看了這個例子,再來了解swing組件監聽的例子就簡單多了,因爲原理完全一樣,都是有事件和事件處理者管理和調用觀察者的方法。

3.swing組件監聽的例子

具體看下代碼:

在這裏插入圖片描述

這個例子比較強大,既體現了委託註冊觀察者,又有某個觀察者的方法對應某個目標類的具體方法,方法到方法的對應。好好理解,學習,爭取項目中使用下:
Event

public class Event {
    //事件源,事件是由誰發起的保存起來
    private Object source;
    //事件觸發,要通知誰
    private Object target;
    //事件觸發,要做什麼動作,回調
    private Method callback;
    //事件的名稱,觸發的是什麼事件
    private String trigger;
    //事件觸發的時間
    private long time;

    public Event(Object target, Method callback) {
        this.target = target;
        this.callback = callback;
    }

    public Event setSource(Object source) {
        this.source = source;
        return this;
    }

    public Event setTime(long time) {
        this.time = time;
        return this;
    }
    public Event setTrigger(String trigger) {
        this.trigger = trigger;
        return this;
    }
    public Object getSource() {
        return source;
    }


    public long getTime() {
        return time;
    }

    public Object getTarget() {
        return target;
    }

    public Method getCallback() {
        return callback;
    }

    @Override
    public String toString() {
        return "Event{" + "\n" +
                "\tsource=" + source.getClass() + ",\n" +
                "\ttarget=" + target.getClass() + ",\n" +
                "\tcallback=" + callback + ",\n" +
                "\ttrigger='" + trigger + "',\n" +
                "\ttime=" + time + "'\n" +
                '}';
    }
}

EventLisenter

public class EventLisenter {
    //JDK底層的Lisenter通常也是這樣來設計的
    protected Map<String, Event> events = new HashMap<String, Event>();

    //事件名稱和一個目標對象來觸發事件
    public void addLisenter(String eventType, Object target) {
        try {
            this.addLisenter(
                    eventType,
                    target,
                    target.getClass().getMethod("on" + toUpperFirstCase(eventType), Event.class));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void addLisenter(String eventType, Object target, Method callback) {
        //註冊事件
        events.put(eventType, new Event(target, callback));
    }


    //觸發,只要有動作就觸發
    private void trigger(Event event) {
        event.setSource(this);
        event.setTime(System.currentTimeMillis());

        try {
            //發起回調
            if (event.getCallback() != null) {
                //用反射調用它的回調函數
                event.getCallback().invoke(event.getTarget(), event);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //事件名稱觸發
    protected void trigger(String trigger) {
        if (!this.events.containsKey(trigger)) {
            return;
        }
        trigger(this.events.get(trigger).setTrigger(trigger));
    }

    //邏輯處理的私有方法,首字母大寫
    private String toUpperFirstCase(String str) {
        char[] chars = str.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }

}

Mouse

public class Mouse extends EventLisenter {

    public void click(){
        System.out.println("調用單擊方法");
        this.trigger(MouseEventType.ON_CLICK);
    }

    public void doubleClick(){
        System.out.println("調用雙擊方法");
        this.trigger(MouseEventType.ON_DOUBLE_CLICK);
    }

    public void up(){
        System.out.println("調用彈起方法");
        this.trigger(MouseEventType.ON_UP);
    }

    public void down(){
        System.out.println("調用按下方法");
        this.trigger(MouseEventType.ON_DOWN);
    }

    public void move(){
        System.out.println("調用移動方法");
        this.trigger(MouseEventType.ON_MOVE);
    }

    public void wheel(){
        System.out.println("調用滾動方法");
        this.trigger(MouseEventType.ON_WHEEL);
    }

    public void over(){
        System.out.println("調用懸停方法");
        this.trigger(MouseEventType.ON_OVER);
    }

    public void blur(){
        System.out.println("調用獲焦方法");
        this.trigger(MouseEventType.ON_BLUR);
    }

    public void focus(){
        System.out.println("調用失焦方法");
        this.trigger(MouseEventType.ON_FOCUS);
    }
}

MouseEventCallback

/**
 * 自己寫的邏輯,用於回調
 * Created by Tom.
 */
public class MouseEventCallback {

    public void onClick(Event e){
        System.out.println("===========觸發鼠標單擊事件==========" + "\n" + e);
    }

    public void onDoubleClick(Event e){
        System.out.println("===========觸發鼠標雙擊事件==========" + "\n" + e);
    }

    public void onUp(Event e){
        System.out.println("===========觸發鼠標彈起事件==========" + "\n" + e);
    }

    public void onDown(Event e){
        System.out.println("===========觸發鼠標按下事件==========" + "\n" + e);
    }

    public void onMove(Event e){
        System.out.println("===========觸發鼠標移動事件==========" + "\n" + e);
    }

    public void onWheel(Event e){
        System.out.println("===========觸發鼠標滾動事件==========" + "\n" + e);
    }

    public void onOver(Event e){
        System.out.println("===========觸發鼠標懸停事件==========" + "\n" + e);
    }

    public void onBlur(Event e){
        System.out.println("===========觸發鼠標失焦事件==========" + "\n" + e);
    }

    public void onFocus(Event e){
        System.out.println("===========觸發鼠標獲焦事件==========" + "\n" + e);
    }

}

MouseEventType

public interface MouseEventType {
    //單擊
    String ON_CLICK = "click";

    //雙擊
    String ON_DOUBLE_CLICK = "doubleClick";

    //彈起
    String ON_UP = "up";

    //按下
    String ON_DOWN = "down";

    //移動
    String ON_MOVE = "move";

    //滾動
    String ON_WHEEL = "wheel";

    //懸停
    String ON_OVER = "over";

    //失焦
    String ON_BLUR = "blur";

    //獲焦
    String ON_FOCUS = "focus";
}

MouseEventTest

public class MouseEventTest {
    public static void main(String[] args) {

        MouseEventCallback callback = new MouseEventCallback();

        Mouse mouse = new Mouse();

        //@誰?  @回調方法
        mouse.addLisenter(MouseEventType.ON_CLICK,callback);
        mouse.addLisenter(MouseEventType.ON_FOCUS,callback);

        mouse.click();

        mouse.focus();


    }
}
public class Keybord extends EventLisenter {

    public void down(){

    }

    public void up(){

    }

}

運行結果:
在這裏插入圖片描述

4.spring中listener實現觀察者模式的例子

Spring的事件機制用到了觀察者模式。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
在Spring中各個listener相當於觀察者。event事件相當於目標,觀察者,而容器用來管理和註冊觀察者,發佈事件。

具體的演示代碼如下:
在這裏插入圖片描述

事件類:OrderEvent 相當於目標類

public class OrderEvent extends ApplicationEvent {

    public OrderEvent(Object source) {
        super(source);
    }
}

OrderSmsListener相當於觀察者

@Component
public class OrderSmsListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent orderEvent) {
        System.out.println("orderSmsListener receive event from " + orderEvent.getSource());
    }
}

業務類:

@Service
public class OrderService {

    @Autowired
    private ApplicationContext applicationContext;


    public void order() {
        applicationContext.publishEvent(new OrderEvent("orderService"));
    }
}

另外spring容器相當於委託類。
測試:

@RunWith(SpringRunner.class)
@SpringBootTest
public class PatternApplicationTest {

    @Autowired
    private OrderService orderService;

    @Test
    public void contextLoads() {
    }


    @Test
    public void testOrder() {
        orderService.order();
    }

}

運行結果:
在這裏插入圖片描述
顯示,事件發佈後,監聽這就能觀察到這個事件了,可以做更多的操作。

5.springboot中集成谷歌的Guava,通過eventBus實現訂閱發佈功能

EventBus無需實現複雜的事件、監聽者、發佈者。前面講的是面向類,現在:Guava是面向是方法,更加強大。能夠輕鬆落地觀察模式的一種解決方案。

演示個例子:
引入Guava的包:

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>

EventBus 事件總線

//api封裝
public class MyEventBus {

    /** 事件任務總線 */
    private final static EventBus tiemEventBus = new EventBus();
    /**
     * 觸發同步事件
     *
     * @param event
     */
    public static void post(Object event) {
        tiemEventBus.post(event);
    }
    /**
     * 註冊事件處理器
     *
     * @param handler
     */
    public static void register(Object handler) {
        tiemEventBus.register(handler);
    }
    /**
     * 註銷事件處理器
     *
     * @param handler
     */
    public static void unregister(Object handler) {
        tiemEventBus.unregister(handler);
    }
}

消息實體類

public class Message {
    private MessageType messageType;
    private String messageContent;

    public Message(MessageType messageType, String messageContent) {
        this.messageType = messageType;
        this.messageContent = messageContent;
    }
    public MessageType getMessageType() {
        return messageType;
    }
    public void setMessageType(MessageType messageType) {
        this.messageType = messageType;
    }
    public String getMessageContent() {
        return messageContent;
    }
    public void setMessageContent(String messageContent) {
        this.messageContent = messageContent;
    }

    public enum MessageType {
        OPENDOOR(1, "openDoor"),
        CLOSEDOOR(2,"closeDoor");
        private int code;
        private String value;

        MessageType(int code, String value) {
            this.code = code;
            this.value = value;
        }
        public int getCode() {
            return code;
        }
        public void setCode(int code) {
            this.code = code;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        }
}

事件監聽者

@Component
abstract class MyApplicationListener implements ApplicationListener<ApplicationPreparedEvent> {
    /**
     *  ApplicationPreparedEvent 上下文準備事件
     * @param applicationPreparedEvent
     */
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
        ConfigurableApplicationContext applicationContext = applicationPreparedEvent.getApplicationContext();
        MyApplicationListener bean = applicationContext.getBean(this.getClass());
        System.out.println("regist listener to eventBus...."+bean);
        MyEventBus.register(bean);
    }
}

訂閱者(也即監聽者)繼承至MyApplicationListener。

@Component
public class MyListentenerSubscribe extends MyApplicationListener{
    @Subscribe
    public void on(Message message){
        System.out.println("subscribe message->  messgeType:"+message.getMessageType()+"\n messageContent:"+message.getMessageContent());
    }
}

測試:

@RestController
public class EventPublishCtrl extends LogBase {
    @GetMapping("/publish")
    public void publishEvent() {
        log.info("this publish method...");
        MyEventBus.post(new Message(Message.MessageType.OPENDOOR,"芝麻開門!"));
    }
}

啓動項目,然後調用發佈方法:
在這裏插入圖片描述

訂閱者收到具體的消息類型,以及消息內容。


三、觀察者模式的本質

在這裏插入圖片描述
在這裏插入圖片描述

上面主要介紹了觀察者模式的基本經典結構;入門小demo;使用的2種方式;觀察者模式變形寫法;java中封裝好的觀察者模式使用方式;不依賴抽象觀察者的方式,比如使用反射委託例子,swing使用觀察者的例子,spring中listener觀察者模式實現的例子,springboot 的eventBus的觀察者模式的例子. 最後說下觀察者模式的本質。

自此,觀察者模式總算寫完了,其實後續可能會有加上mq的例子,還沒深入學習,先這樣吧。

參考文章:
https://blog.csdn.net/gdutxiaoxu/article/details/51824769
https://blog.csdn.net/fw19940314/article/details/100010397
https://www.cnblogs.com/wkzhao/p/10229283.html


個人微信公衆號:
搜索: 怒放de每一天
不定時推送相關文章,期待和大家一起成長!!
在這裏插入圖片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章