觀察者模式,又被稱為發布—訂閱模式、源—收聽者模式,是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依與它的觀察者物件,並且在它本身的狀態改變時主動發出同時,此種模式通常被用來實現事件處理系統。
觀察者模式一般包含以下四種角色:
抽象主題角色Watched:把所有對觀察者對象的引用保存在一個集合中,每個抽象主題角色都可以有任意數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察這角色。
抽象觀察者角色Watcher:為所有具體的觀察者定義一個接口,在得到主題的通知更新自己。
具體主題角色ConcreteWatched:在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。
具體觀察者角色ConcreteWatcher:該角色實現抽象觀察者角色所要求的更新接口,以便使自身的狀態與主題的狀態相協調。
觀察者模式有很多實現方式,從根本上說,該模式必須包含兩個角色,觀察者和被觀察對象。比較直觀的一種是使用“注冊”—“通知”—“撤銷通知”的形式。
觀察者將自己注冊到被觀察對象中,被觀察者將觀察者放在一個容器中。
被觀察者對象發生某種變化時,從容器中得到所有被注冊過的觀察者,將變化通知觀察者。
觀察者將自己注冊到被觀察者的容器時,被觀察者不應該過問觀察者的具體類型,而是應該使用觀察者的接口。這樣的好處是:假定程序還有別的觀察者,那麼只要這個觀察者也是相同的接口即可。一個被觀察者可以對應多個觀察者,當被觀察者發生變化的時候,他可以將消息一一通知給所有的觀察者。如下圖所示:
具體到一個應用場景就是:讀者訂閱新聞,每一個讀者都可以通過regist來訂閱新聞,通過remove來取消訂閱,當有新聞到來時,使用sendNews來通知每一位訂閱的讀者來收取新聞。
下面用代碼來實現上面的實例:
抽象主題角色Watched:新聞接口,定義訂閱、取消訂閱和推送新聞三個方法
public interface Watched { //訂閱新聞 void registeSubscriber(Watcher f_subscriber); //取消訂閱 void removeSubscriber(Watcher f_subscriber); //推送新聞 void sendNews(); }
抽象觀察者角色Watcher:讀者接口,定義了獲取新聞的方法
public interface Watcher { //獲取新聞 void updateNews(); }
具體主題角色ConcreteWatched,即被觀察者,實現了Watched接口:
import java.util.ArrayList; import java.util.List; /* * 具體被觀察者 * 新聞發布者 */ public class ConcreteWatched implements Watched { private List<Watcher> subList=new ArrayList<Watcher>(); @Override public void registeSubscriber(Watcher f_subscriber) { subList.add(f_subscriber); } @Override public void removeSubscriber(Watcher f_subscriber) { subList.remove(f_subscriber); } @Override public void sendNews() {
System.out.println("開始本輪新聞推送!"); for (Watcher watcher : subList) { watcher.updateNews(); } System.out.println("本次新聞推送結束!"); } }
具體觀察者角色ConcreteWatcher,即觀察者對象
/* * 具體的觀察者 * 讀者 */ public class ConcreteWatcher implements Watcher { private String username; public ConcreteWatcher(String username) { super(); this.username = username; } @Override public void updateNews() { System.out.println(username+"獲取到最新的新聞了!"); } }
下面是一個測試用例:
import org.junit.Test; public class WatchTest { @Test public void test() { ConcreteWatched concreteWatched=new ConcreteWatched(); Watcher watcher1=new ConcreteWatcher("讀者A"); Watcher watcher2=new ConcreteWatcher("讀者B"); Watcher watcher3=new ConcreteWatcher("讀者C"); //訂閱新聞 concreteWatched.registeSubscriber(watcher1); concreteWatched.registeSubscriber(watcher2); concreteWatched.registeSubscriber(watcher3); //推送新聞 concreteWatched.sendNews(); //讀者A取消訂閱 concreteWatched.removeSubscriber(watcher1); //再次推送新聞 concreteWatched.sendNews(); } }
執行結果: