Java中觀察者模式與委託,還在傻傻分不清
本文分享自華為雲社群《Java中觀察者模式與委託的對比》,作者:小小張自由--張有博。
程式碼背景
一個班級,有兩類學生,A類:不學習,玩,但是玩的東西不一樣,有的是做遊戲,有的是看電視
B類:放哨的學生,專門看老師的動向,如果老師進班了就立即通知大家。
如此就形成了一個需求,放哨的學生要通知所有玩的學生:老師來了,而不同的學生有不同的反應,有的馬上把電視關閉,有的停止玩遊戲。
觀察者模式
介紹
觀察者模式:定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有的觀察者物件,使他們能夠自動更新自己。
主要解決:一個物件狀態改變給其他物件通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
何時使用:一個物件(目標物件)的狀態發生改變,所有的依賴物件(觀察者物件)都將得到通知,進行廣播通知。
如何解決:使用面向物件技術,可以將這種依賴關係弱化。
關鍵程式碼:在抽象類裡有一個 ArrayList 存放觀察者們。
實現
觀察者(學生)
``` /* * 抽象的觀察者 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/10 - 15:32 / public interface Observer {
public abstract void updateState();
}
/* * 具體的觀察者 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/10 - 15:39 / public class ConcreteObserver implements Observer{
//觀察者的姓名
private String name;
//觀察者的狀態
private String observerState;
//明確具體的通知者
private ConcreteSubject subject;
//get set方法省略
public ConcreteObserver(String name, ConcreteSubject subject) {
this.name = name;
this.subject = subject;
}
@Override
public void updateState() {
observerState=subject.getSubjectState();
System.out.println(name+"在打遊戲");
String str=String.format("觀察者%s的:新狀態是%s", name,observerState);
System.out.println(str);
}
} /* * 具體的觀察者 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/10 - 15:39 / public class ConcreteObserver2 implements Observer{
//觀察者的姓名
private String name;
//觀察者的狀態
private String observerState;
//明確具體的通知者
private ConcreteSubject subject;
//get set方法省略
public ConcreteObserver2(String name, ConcreteSubject subject) {
this.name = name;
this.subject = subject;
}
@Override
public void updateState() {
observerState=subject.getSubjectState();
System.out.println(name+"在看電視");
String str=String.format("觀察者%s:新狀態是%s", name,observerState);
System.out.println(str);
}
} ```
通知者(老師)
``` /* * 抽象的通知者 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/10 - 15:30 / public abstract class Subject {
//管理觀察者的集合
private List<Observer> observers=new ArrayList<>();
//增加觀察者
public void add(Observer observer){
observers.add(observer);
}
//減少觀察者
public void detach(Observer observer){
observers.remove(observer);
}
/**
* 通知所有的觀察者
*/
public void notifyMsg(){
for (Observer observer : observers) {
observer.updateState();
}
}
} /* * 具體的通知者 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/10 - 15:38 / public class ConcreteSubject extends Subject {
//通知者的狀態
private String subjectState;
//get set方法
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
} ```
Main方法
``` /* * 控制檯Main方法 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/10 - 15:48 / public class MainTest {
public static void main(String[] args) {
//建立一個主題/通知者
ConcreteSubject subject=new ConcreteSubject();
//new出觀察者(學生)
ConcreteObserver studentZhang = new ConcreteObserver("小張", subject);
ConcreteObserver studentLiu = new ConcreteObserver("小劉", subject);
ConcreteObserver studentWang = new ConcreteObserver("小王", subject);
//將觀察者新增到通知佇列裡
subject.add(studentZhang);
subject.add(studentLiu);
subject.add(studentWang);
//通知者(老師)狀態修改,通知每個學生
subject.setSubjectState("老師回來了,我要好好學習");
subject.notifyMsg();
System.out.println("-----------");
}
} ```
委託
介紹
委託可以看做是函式的抽象,是函式的“類”。委託的例項將代表一個具體的函式
一個委託可以搭載多個方法,所有的方法被依次喚起。可以使委託物件所搭載的方法並不需要屬於同一類。
委託事件模型可以由三個元件定義:事件、事件源和事件偵聽器。
委託的實現簡單來講就是用反射來實現的。
實現
觀察者
``` /* * 監聽器/觀察者 玩遊戲 * 事件監聽器 * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:17 / public class PlayingGameListener {
public PlayingGameListener(){
System.out.println("我正在玩遊戲 開始時間"+new Date());
}
public void stopPlayingGame(Date date){
System.out.println("老師來了,快回到座位上,結束時間"+date);
}
}
/* * 監聽器/觀察者 看電視 * 事件監聽器 * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:17 / public class WatchingTVListener {
public WatchingTVListener(){
System.out.println("我正在看電視 "+new Date());
}
public void stopWatchingTV(Date date){
System.out.println("老師來了,快關閉電視 。 結束時間"+date);
}
} ```
通知者
``` /* * 通知者的抽象類 * 事件源 * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:15 / public abstract class Notifier {
//每個通知者都有一個需要通知的佇列(通知:物件、方法、引數)
private EventHandler eventHandler=new EventHandler();
public EventHandler getEventHandler() {
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler) {
this.eventHandler = eventHandler;
}
//增加需要幫忙放哨的學生
public abstract void addListener(Object object,String methodName,Object...args);
//告訴所有要幫忙放哨的學生:老師來了
public abstract void notifyX();
} /* * 通知者的子類,放哨人 * 事件源 * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:15 / public class GoodNotifier extends Notifier {
@Override
public void addListener(Object object, String methodName, Object...args) {
System.out.println("有新的同學委託盡職盡責的放哨人!");
this.getEventHandler().addEvent(object, methodName, args);
}
@Override
public void notifyX() {
System.out.println("盡職盡責的放哨人告訴所有需要幫忙的同學:老師來了");
try{
//優化:非同步通知
this.getEventHandler().notifyX();
}catch(Exception e){
e.printStackTrace();
}
}
} ```
事件
``` /* * 抽象出的事件類,也可以稱為方法類 * 事件 * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:03 / public class Event {
//要執行方法的物件
private Object object;
//要執行的方法名稱
private String methodName;
//要執行方法的引數
private Object[] params;
//要執行方法的引數型別
private Class[] paramTypes;
//若干setter getter
public Object getObject() {
return 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;
}
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 void invoke() throws Exception{
//通過class,method,paramTypes 確定執行哪個類的哪個方法
Method method=object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
if(null==method){
return;
}
//方法執行
method.invoke(this.getObject(), this.getParams());
}
} ```
事件處理
``` /* * 管理哪些事件需要執行 * 管理事件 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:03 / public class EventHandler {
//是用一個List
private List<Event> objects;
//新增某個物件要執行的事件,及需要的引數
public void addEvent(Object object,String methodName,Object...args){
objects.add(new Event(object,methodName,args));
}
public EventHandler(){
objects=new ArrayList<Event>();
}
//通知所有的物件執行指定的事件
public void notifyX() throws Exception{
for(Event e : objects){
e.invoke();
}
}
} ```
Main方法
``` /* * 啟動類 * * @author Promsing(張有博) * @version 1.0.0 * @since 2022/5/8 - 11:19 / public class EventMain {
public static void main(String[] args) {
//建立一個盡職盡責的放哨者
Notifier goodNotifier = new GoodNotifier();
//建立一個玩遊戲的同學,開始玩遊戲
PlayingGameListener playingGameListener = new PlayingGameListener();
//建立一個看電視的同學,開始看電視
WatchingTVListener watchingTVListener = new WatchingTVListener();
//玩遊戲的同學告訴放哨的同學,老師來了告訴一下
goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看電視的同學告訴放哨的同學,老師來了告訴一下
goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
try {
//一點時間後
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//老師出現,放哨的人通知所有要幫忙的同學:老師來了
goodNotifier.notifyX();
}
} ```
總結
1.先有觀察者模式後有委託事件技術
2.觀察者模式只能通知繼承 Observer類 的子類,也可以將Observer改成介面
for (Observer observer : observers) {
observer.updateState();
}
3.委託可以通知任何類的任何方法。反射、everone
Method method=object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
if(null==method){
return;
}
method.invoke(this.getObject(), this.getParams());
4.委託與觀察者比多了一個事件執行者,解除觀察者與通知者的耦合,可以做到通知任何物件的任何方法。讓A類學生和B類學生完全解耦,即A類完全不知道B類的學生,卻可以通知B類的學生
6.建立一套觸發機制,可以使用非同步通知
7.觀察者/委託挺像MQ裡邊的訂閱釋出。生產者、佇列、消費者。
- 帶你掌握 C 中三種類成員初始化方式
- 實踐GoF的設計模式:工廠方法模式
- DCM:一個能夠改善所有應用資料互動場景的中介軟體新秀
- 手繪圖解java類載入原理
- 關於加密通道規範,你真正用的是TLS,而非SSL
- 程式碼重構,真的只有複雜化一條路嗎?
- 解讀分散式排程平臺Airflow在華為雲MRS中的實踐
- 透過例項demo帶你認識gRPC
- 帶你聚焦GaussDB(DWS)儲存時遊標使用
- 傳統到敏捷的轉型中,誰更適合做Scrum Master?
- 輕鬆解決研發知識管理難題
- Java中觀察者模式與委託,還在傻傻分不清
- 如何使用Python實現影象融合及加法運算?
- 什麼是強化學習?
- 探索開源工作流引擎Azkaban在MRS中的實踐
- GaussDB(DWS) NOT IN優化技術解密:排他分析場景400倍效能提升
- Java中觀察者模式與委託,還在傻傻分不清
- Java中的執行緒到底有哪些安全策略
- 一圖詳解java-class類檔案原理
- Java中的執行緒到底有哪些安全策略