網(wǎng)站建站報價表什么軟件引流客源最快
觀察者模式
目錄
- 觀察者模式
- 1、什么是觀察者模式?
- 2、觀察者模式優(yōu)缺點及注意事項?
- 3、觀察者模式實現(xiàn)?
- 4、手寫線程安全的觀察者模式?
1、什么是觀察者模式?
??- 實例:現(xiàn)實生活中很多事物都是依賴存在的,一個發(fā)生變化會影響很多事物。比如油價上漲,關(guān)系很多企業(yè),很多家庭;紅綠燈發(fā)生變化時,人們會停止,會前進等。
??- 觀察者模式 (Observer Pattern) :是一種一對多的依賴關(guān)系,讓多個觀察對象同時監(jiān)聽某一個主題對象,當主題對象的狀態(tài)發(fā)生變化時,會自動通知所有觀察者,使得它們能夠自動更新自己。適用于當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都需要得到通知的情況。
??- 觀察者模式具體的角色 :實現(xiàn)觀察者模式時要注意具體目標對象和具體觀察者對象之間不能直接調(diào)用,否則將使兩者之間緊密耦合起來,這違反了面向?qū)ο蟮脑O(shè)計原則。
- 抽象主題(Subject)角色:也叫抽象目標類,它提供了一個用于保存觀察者對象的聚集類和增加、刪除觀察者對象的方法,以及通知所有觀察者的抽象方法。
- 具體主題(Concrete Subject)角色:也叫具體目標類,它實現(xiàn)抽象目標中的通知方法,當具體主題的內(nèi)部狀態(tài)發(fā)生改變時,通知所有注冊過的觀察者對象。繼承Subject類,在這里實現(xiàn)具體業(yè)務(wù),在具體項目中,該類會有很多變種。
- 抽象觀察者(Observer)角色:它是一個抽象類或接口,它包含了一個更新自己的抽象方法,當接到具體主題的更改通知時被調(diào)用。
- 具體觀察者(Concrete Observer)角色:實現(xiàn)抽象觀察者中定義的抽象方法,以便在得到目標的更改通知時更新自身的狀態(tài)。
2、觀察者模式優(yōu)缺點及注意事項?
- 優(yōu)點:
??1.降低了目標與觀察者之間的耦合關(guān)系,兩者之間是抽象耦合關(guān)系.符合依賴倒置原則
??2.目標與觀察者之間建立了一套觸發(fā)機制
- 缺點:
??1.目標與觀察者之間的依賴關(guān)系并沒有完全解除,而且有可能出現(xiàn)循環(huán)引用
??2.當觀察者對象很多時,通知的發(fā)布會花費很多時間,影響程序的效率
- 注意事項:
??1.JDK8 中java.util.Observable 類和 java.util.Observer 接口定義了觀察者模式,后面版本舍棄了,因為在使用異步處理的情況下,線程不安全。
??2.要注意循環(huán)調(diào)用情況,避免死鎖。
??3.可以去觀察消息隊列實現(xiàn),典型的觀察者模式實現(xiàn)。
3、觀察者模式實現(xiàn)?
模仿jdk8中 Observable 類 和 Observer接口實現(xiàn)
- 目標類
public class Subject {private Vector<Observer> obs = new Vector<>();public void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.add(o);}}public void deleteObserver(Observer o) {obs.remove(o);}public void notifyObservers() {for(Observer observer : this.obs) {observer.resopnse();}}
}
- 具體實現(xiàn)目標類
public class ConcreteSubject extends Subject{// 具體實現(xiàn)public void doSomethings(){// 一些其它邏輯處理System.out.println("-----被觀察者發(fā)生了改變------");super.notifyObservers();}
}
- 觀察者類
public interface Observer {// 響應(yīng)void resopnse();
}
- 具體觀察者類
public class ConcreteObserver implements Observer{private int num;public ConcreteObserver(int num) {this.num = num;}@Overridepublic void resopnse() {System.out.println("觀察者"+num+"做出改變");}
}
- 執(zhí)行任務(wù)
public class Main {public static void main(String[] args) {
// --------------- 觀察者模式 -----------------// 先創(chuàng)建多個觀察者ConcreteSubject concreteSubject = new ConcreteSubject();for (int a = 0; a < 10; a++) {concreteSubject.addObserver(new ConcreteObserver(a+1));}concreteSubject.doSomethings();}
4、手寫線程安全的觀察者模式?
設(shè)定場景:手機花費快沒錢了,運營商通知你要繳費了。 這里運營商就是被觀察者,用戶就是觀察者,用戶的手機號就是注冊的一個過程,運營商通知用戶。
使用jdk中com.util.concurrent提供的線程安全的類:CountDownLatch,CountDownLatch就一個線程同步工具,它相當于一個倒序計數(shù)器,用來協(xié)調(diào)多個線程的執(zhí)行。多個線程通過調(diào)用它們所共享的計數(shù)器CountDownLatch的countDown方法來讓計數(shù)器減1。通過await方法來阻塞當前線程,直到計數(shù)器變成0。達到線程阻塞直至其他線程執(zhí)行完成被重新喚醒。主要有三個方法:
1.構(gòu)造函數(shù),初始化state的值,state等于同步線程數(shù)
2.await(),讓線程阻塞
3.countDown(),計數(shù)器(state)減1的方法。
public class Main {public static void main(String[] args) {// --------------- 安全的觀察者模式 --------------// 設(shè)定場景:手機花費快沒錢了,運營商通知你要繳費了。 這里運營商就是被觀察者,用戶就是觀察者,用戶的手機號就是注冊的一個過程,運營商通知用戶。ConcreteTelecomOperator telecomOperator = new ConcreteTelecomOperator("中國電信");telecomOperator.addUser(new ConcreteUser("張三"));telecomOperator.addUser(new ConcreteUser("李四"));telecomOperator.addUser(new ConcreteUser("王五"));telecomOperator.addUser(new ConcreteUser("小明"));telecomOperator.doSomeThing("通知您,您已欠費請及時繳費。祝你生活愉快!");}
}
/*** @program: practice_tools* @description: 運營商目標類* @author: tiezhu* @create: 2025-01-20 09:58**/
public class TelecomOperator {private String name;private ConcurrentMap<String, User> obs;public TelecomOperator(String name) {this.name = name;obs = new ConcurrentHashMap<>();}public void addUser(User u){if (u == null)throw new NullPointerException();if (!obs.containsKey(u.getName())) {obs.put(u.getName(),u);}}public void removeUser(User u){obs.remove(u.getName());}public void notifyObservers(String content) {try {long beginTime = System.currentTimeMillis();CountDownLatch latch = new CountDownLatch(obs.size());for (User user : obs.values()) {user.response(content);latch.countDown();}latch.await();long endTime = System.currentTimeMillis();System.out.println(name + "消息發(fā)送完畢,耗時:" + (endTime - beginTime));System.out.println();} catch (InterruptedException e) {e.printStackTrace();}}
}
/*** @program: practice_tools* @description: 具體目標類* @author: tiezhu* @create: 2025-01-20 11:43**/
public class ConcreteTelecomOperator extends TelecomOperator{public ConcreteTelecomOperator(String name) {super(name);}public void doSomeThing(String content){System.out.println("開始發(fā)布信息:");super.notifyObservers(content);}
}
/*** 用戶觀察者*/
public interface User {void response(String content);String getName();
}
/*** @program: practice_tools* @description: 觀察者實體類* @author: tiezhu* @create: 2025-01-20 10:04**/
public class ConcreteUser implements User{private String name;public ConcreteUser(String name) {this.name = name;}@Overridepublic void response(String content) {System.out.println("接收到了消息為:"+content);System.out.println(name + "準備去繳費了");}@Overridepublic String getName() {return name;}
}
結(jié)果:執(zhí)行結(jié)果和創(chuàng)建順序不同,不用按照創(chuàng)建順序執(zhí)行完再執(zhí)行了。