文章收錄在 GitHub JavaKeeper ,N線互聯網開發必備技能兵器譜
在軟件系統中常常會有這樣的需求:若是一個對象的狀態發生改變,某些與它相關的對象也要隨之作出相應的變化。html
觀察者模式是使用頻率較高的設計模式之一。java
觀察者模式包含觀察目標和觀察者兩類對象,一個目標能夠有任意數目的與之相依賴的觀察者,一旦觀察目標的狀態發生改變,全部的觀察者都將獲得通知。git
觀察者模式(Observer Pattern): 定義對象間一種一對多的依賴關係,使得當每個對象改變狀態,則全部依賴於它的對象都會獲得通知並自動更新。 github
觀察者模式是一種對象行爲型模式。設計模式
觀察者模式的別名包括髮布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。安全
細究的話,發佈訂閱和觀察者有些不一樣,能夠理解成發佈訂閱模式屬於廣義上的觀察者模式。微信
update()
,所以又稱爲抽象觀察者。
再記錄下 UML 類圖的注意事項,這裏個人 Subject 是抽象方法,因此用斜體,抽象方法也要用斜體,具體的各類箭頭意義,我以前也總結過《設計模式前傳——學設計模式前你要知道這些》(被網上各類帖子毒害過的本身,認真記錄~~~)。多線程
一、定義觀察者接口app
interface Observer { public void update(); }
二、定義被觀察者測試
abstract class Subject { private Vector<Observer> obs = new Vector(); public void addObserver(Observer obs){ this.obs.add(obs); } public void delObserver(Observer obs){ this.obs.remove(obs); } protected void notifyObserver(){ for(Observer o: obs){ o.update(); } } public abstract void doSomething(); }
三、具體的被觀察者
class ConcreteSubject extends Subject { public void doSomething(){ System.out.println("被觀察者事件發生改變"); this.notifyObserver(); } }
四、具體的被觀察者
class ConcreteObserver1 implements Observer { public void update() { System.out.println("觀察者1收到信息,並進行處理"); } } class ConcreteObserver2 implements Observer { public void update() { System.out.println("觀察者2收到信息,並進行處理"); } }
五、客戶端
public class Client { public static void main(String[] args){ Subject sub = new ConcreteSubject(); sub.addObserver(new ConcreteObserver1()); //添加觀察者1 sub.addObserver(new ConcreteObserver2()); //添加觀察者2 sub.doSomething(); } }
輸出
被觀察者事件發生改變 觀察者1收到信息,並進行處理 觀察者2收到信息,並進行處理
經過運行結果能夠看到,咱們只調用了 Subject
的方法,但同時兩個觀察者的相關方法都被調用了。仔細看一下代碼,其實很簡單,就是在 Subject
類中關聯一下 Observer
類,而且在 doSomething()
方法中遍歷一下 Observer
的 update()
方法就好了。
觀察者模式在 Java 語言中的地位很是重要。在 JDK 的 java.util 包中,提供了 Observable 類以及 Observer 接口,它們構成了 JDK 對觀察者模式的支持(能夠去查看下源碼,寫的比較嚴謹)。but,在 Java9 被棄用了。
Spring 事件驅動模型也是觀察者模式很經典的應用。就是咱們常見的項目中最多見的事件監聽器。
Spring 也爲咱們提供了不少內置事件,ContextRefreshedEvent
、ContextStartedEvent
、ContextStoppedEvent
、ContextClosedEvent
、RequestHandledEvent
。
ApplicationContext
是 Spring 中的核心容器,在事件監聽中 ApplicationContext 能夠做爲事件的發佈者,也就是事件源。由於 ApplicationContext 繼承自 ApplicationEventPublisher。在 ApplicationEventPublisher
中定義了事件發佈的方法:publishEvent(Object event)
一、定義事件
public class MyEvent extends ApplicationEvent { public MyEvent(Object source) { super(source); System.out.println("my Event"); } }
二、實現事件監聽器
@Component class MyListenerA implements ApplicationListener<MyEvent> { public void onApplicationEvent(MyEvent AyEvent) { System.out.println("ListenerA received"); } } @Component class MyListenerB implements ApplicationListener<MyEvent> { public void onApplicationEvent(MyEvent AyEvent) { System.out.println("ListenerB received"); } }
三、事件發佈者
@Component public class MyPublisher implements ApplicationContextAware { private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } public void publishEvent(ApplicationEvent event){ System.out.println("publish event"); applicationContext.publishEvent(event); } }
四、測試,先用註解方式將 MyPublisher 注入 Spring
@Configuration @ComponentScan public class AppConfig { @Bean(name = "myPublisher") public MyPublisher myPublisher(){ return new MyPublisher(); } }
public class Client { @Test public void main() { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyPublisher myPublisher = (MyPublisher) context.getBean("myPublisher"); myPublisher.publishEvent(new MyEvent(this)); } }
五、輸出
my Event publish event ListenerA received ListenerB received
設計模式真的只是一種設計思想,不須要非得有多個觀察者才能夠用觀察者模式,只有一個觀察者,我也要用。
再舉個栗子,我是作廣告投放的嘛(廣告投放的商品文件通常爲 xml),假如個人廣告位有些空閒流量,這我得利用起來呀,因此我就從淘寶客或者拼夕夕的多多客上經過開放的 API 獲取一些,這個時候我也能夠用觀察者模式,每次請求 10 萬條商品,我就生成一個新的商品文件,這個時候我也能夠用觀察者模式,獲取商品的類是被觀察者,寫商品文件的是觀察者,當商品夠10萬條了,就通知觀察者從新寫到一個新的文件。
大佬可能覺這麼實現有點費勁,不用設計模式也好,或者用消息隊列也好,其實都只是一種手段,選擇適合本身業務的,開心就好。