關注公衆號JavaStorm 獲取最新文章。java
觀察者模式(有時又被稱爲模型(Model)-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理全部相依於它的觀察者物件,而且在它自己的狀態改變時主動發出通知。這一般透過呼叫各觀察者所提供的方法來實現。此種模式一般被用來實現事件處理系統。git
源代碼地址:https://github.com/UniqueDong/zero-design-stugithub
報社的業務就是出版報紙,客戶訂閱該報社,那麼只要有新的報紙出版就會給訂閱報社的客戶送來,只要一直是報社的訂閱客戶,就能一直收到新報紙。編程
當你不想訂閱,取消就能夠,就不會再收到通知。設計模式
報社提供訂閱與取消訂閱的入口。ide
實際上這裏就是一個觀察者模式的例子,報社充當 Subject 主題角色,訂閱報社的客戶就是 Observer 觀察者角色。出版者-主題,訂閱者-觀察者。測試
首先咱們定義 Subject 主題角色報社 NewspaperSubject。主要提供 註冊觀察者、刪除觀察者、通知全部觀察者方法。this
定義包報紙對象 Newspaperspa
public class Newspaper implements Serializable { private LocalDateTime reportTime; private String data; public LocalDateTime getReportTime() { return reportTime; } public void setReportTime(LocalDateTime reportTime) { this.reportTime = reportTime; } public String getData() { return data; } public void setData(String data) { this.data = data; } @Override public String toString() { return "Newspaper{" + "reportTime=" + reportTime + ", data='" + data + '\'' + '}'; } }
定義主題對象設計
public interface NewspaperSubject { /** * 註冊觀察者 * @param observer */ void registerObserver(Observer observer); /** * 移除觀察者 * @param observer */ void removeObserver(Observer observer); /** * 通知全部觀察者 * @param data */ void notifyObservers(Newspaper data); }
同時實現該主題,代碼以下:
import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ChinaNewspaperSubject implements NewspaperSubject { private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private List<Observer> observers; public ChinaNewspaperSubject() { this.observers = new ArrayList<>(); } public void setChange() { Newspaper newspaper = new Newspaper(); newspaper.setReportTime(LocalDateTime.now()); newspaper.setData("發佈新聞"); notifyObservers(newspaper); } @Override public void registerObserver(Observer observer) { ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); try { writeLock.lock(); observers.add(observer); } finally { writeLock.unlock(); } } @Override public void removeObserver(Observer observer) { ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); try { writeLock.lock(); observers.remove(observer); } finally { writeLock.unlock(); } } @Override public void notifyObservers(Newspaper data) { ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock(); try { readLock.lock(); observers.forEach(item -> item.notice(data)); } finally { readLock.unlock(); } } }
而後定義觀察者(Observer)角色 也就是報紙訂閱者
public interface Observer { /** * 接收主題發佈的更新通知 */ void notice(Newspaper data); }
定義觀察者具體實現類:一個香港用戶訂閱報紙
public class HonKongObserver implements Observer { @Override public void notice(Newspaper data) { System.out.println("我收到報社的報紙了:" + "內容是" + data.toString()); } }
最後測試
public class Test { public static void main(String[] args) { //建立報社 ChinaNewspaperSubject newspaperSubject = new ChinaNewspaperSubject(); //建立訂閱者 HonKongObserver honKongObserver = new HonKongObserver(); //訂閱者關注該報社 newspaperSubject.registerObserver(honKongObserver); //報社發佈新報紙。全部逇訂閱者收到報紙 newspaperSubject.setChange(); } }
咱們的JDK內部與爲咱們實現了觀察者模式。只不過咱們的主題須要繼承 jdk 中的主題,觀察者實現對應的Observer 接口。以前咱們說過要多用組合與委託。面向接口編程而不是實現。內置的主題咱們必須繼承,若想更靈活其實咱們本身定義主題接口會更好,而且也並不難。
首先咱們的主題要先繼承 Observerble ,這是jdk內置的。
public class NumsObservable extends Observable { public final static Integer ODD = 1; public final static Integer EVEN = 2; private int data = 0; /** * 獲取對象數據 * * @return */ public int getData() { return data; } /** * 設置數據變化 * 根據數據的變化設置相應的標誌變量,通知給訂閱者 * * @param data */ public void setData(int data) { this.data = data; Integer flag = EVEN; if ((this.data & 0x0001) == 1) { flag = ODD; } setChanged(); // 將變化的變化的標識變量通知給訂閱者 notifyObservers(flag); } }
接着定義咱們的觀察者:分別是偶數與奇數訂閱者。
/** * 奇數內容訂閱類 * Created by jianqing.li on 2017/6/8. */ public class OddObserver implements Observer { /** * 繼承自Observer接口類,update的方法的實現 * * @param o 主題對象 * @param arg notifyObservers(flag);傳來的參數,便是標識變量 */ @Override public void update(Observable o, Object arg) { if (arg == NumsObservable.ODD) { NumsObservable numsObservable = (NumsObservable) o; System.out.println("Data has changed to ODD number " + numsObservable.getData()); } } }
/** * 偶數內容訂閱類:訂閱主題的內容的偶數變化 * Created by jianqing.li on 2017/6/8. */ public class EvenObserver implements Observer { /** * 繼承自Observer接口類,update的方法的實現 * * @param o 主題對象 * @param arg notifyObservers(flag);傳來的參數,便是標識變量 */ @Override public void update(Observable o, Object arg) { if (arg == NumsObservable.EVEN) { NumsObservable numsObservable = (NumsObservable) o; System.out.println("Data has changed to EVEN number " + numsObservable.getData()); } } }
編寫測試
public class ObserverTest { public static void main(String[] args) { // 建立主題 NumsObservable numsObservable = new NumsObservable(); //建立訂閱者 OddObserver oddObserver = new OddObserver(); EvenObserver evenObserver = new EvenObserver(); numsObservable.addObserver(oddObserver); numsObservable.addObserver(evenObserver); //修改主題內容,觸發notifyObservers numsObservable.setData(11); numsObservable.setData(12); numsObservable.setData(13); } }
客官以爲不過能夠訂閱 公衆號 JavaStorm 與點贊,你的訂閱就是最好的觀察者應用。