在上一邊文章中,和大家分享了基於java內置實現的觀察者模式。在本次分享中呢,將會是一個完全自己定義的一個觀察者模式。我們還是以新聞報社爲例子。
在這裏,我們將報社(被觀察者)成爲主題(Subject),觀察者成爲(Observer),在第一篇文章中,我們討論了策略模式,也就是我們要針對接口編程,所以我們首先來定義兩個接口,一個是主題接口,另一個是觀察者接口。
主題接口有什麼內容呢?首先我們應該允許增加新的註冊用戶(訂閱報社的用戶),還有用戶進行取消註冊,還有就是當有新的數據的時候,應該能夠通知所有的註冊了的用戶。因此應該有三個方法。
package demo;
/**
* 觀察者模式的主題接口
* @author wangpeiyu
*
*/
public interface ISubject {
public void register(IObserver observer);
public void remove(IObserver subject);
public void notifys();
}
觀察者接口應該有什麼呢?首先最重要的就是更新相關的數據了。
更新有兩個方法:
方法一:直接通過update方法,將所有的數據以參數的形式傳遞過來
方法二: 直接將持有數據的主題(可觀察者,也就是報社)的對象傳遞過來,觀察者自己通過該對象get想要的數據。
package demo;
/**
* 觀察者模式的觀察者接口
* @author wangpeiyu
*
*/
public interface IObserver {
public void update(String news);
public void update(ISubject subject);
}
下面我們來編寫主題(可觀察者)的實現類。
除了要有接口的註冊、取消註冊和通知所有用戶的方法外,還要什麼方法呢?
我們肯定要有一個更新數據然後觸發通知所有用戶方法的方法,這裏我們就用updateNews(),更新新聞的方法,來表示數據發生了改變,要通知相關的用戶了。package demo;
import java.util.ArrayList;
public class Subject implements ISubject {
//維護觀察者的數組,可以通過遍歷整個數組,達到通知所有註冊了的用戶
private ArrayList<IObserver> observerList;
//新聞信息
private String news;
public Subject() {
// TODO Auto-generated constructor stub
observerList = new ArrayList<IObserver>();
news="";
}
public void updateNews(String news){
this.news=news;
notifys();//更新了新聞之後,應該通知所有的一已經註冊了的用戶,即觀察者
}
/**
* 返回新的新聞信息
* 當在notifys中使用的是第一種傳遞方式的時候,可以使用這個方法,但是,此方法必須要設置爲public
* 該方法的好處在於,用戶可以自定義的獲取自己想要的信息
* 但是將本主題對象傳遞過去,容易暴露信息
* 如果使用的是第二種方式的時候,可以直接將新的新聞信息傳遞出去,此時getNews這個方法可以是私有的
* 這個方法的好處在於,只將所有數據傳遞出去,不容易暴露主題對象的信息
* 不足是全部信息都傳遞出去,有些是用戶不需要的
*
* @return
*/
public String getNews(){
return news;
}
@Override
public void notifys() {
// TODO Auto-generated method stub
for(IObserver observer:observerList)
{
observer.update(this);//這是一種傳遞方式
observer.update(news);//這是另一種方式
}
}
/**
* 註冊觀察者
*/
@Override
public void register(IObserver observer) {
// TODO Auto-generated method stub
if(!observerList.contains(observer))
{
observerList.add(observer);
}
}
/**
* 觀察者取消註冊
*/
@Override
public void remove(IObserver observer) {
// TODO Auto-generated method stub
//找到要取消註冊的觀察者的索引
//如果索引爲-1,則表示該觀察者還沒有註冊,所以不用進行取消註冊
//否則就是已經註冊了,然後進行取消
int index = observerList.indexOf(observer);
if(index!=-1)
{
observerList.remove(index);
}
}
}
接着我們再來實現觀察者的方法。方法註釋已經寫得比較詳細了。
package demo;
public class Observer implements IObserver {
private String name;
private ISubject subject;
/**
* 爲什麼要傳遞Subject對象過來呢????
* 大家可以思考。
* 我個人覺得,傳遞Subject對象過來,可以直接在觀察者中增加註冊和取消註冊的方法,然後在方法裏面調用Subject進行註冊
* 這樣的好處是,可以直接通過觀察者就可以完成了自身的註冊和取消註冊
* 可以避免要註冊的時候,直接使用Subject類註冊再傳遞一個觀察者過去,維護性不強
* @param name
* @param subject
*/
public Observer(String name,ISubject subject) {
// TODO Auto-generated constructor stub
this.name = name;
this.subject = subject;
}
@Override
public void update(String news) {
// TODO Auto-generated method stub
System.out.println(name+" "+news+"\n");//直接將傳遞過來的,自己感興趣的數據進行輸出
}
@Override
public void update(ISubject subject) {
// TODO Auto-generated method stub
if(subject!=null)
{
if(subject instanceof Subject)
{
Subject subjectEntires = (Subject)subject;
System.out.println(name+" "+subjectEntires.getNews()+"\n");//這時候通過暴露出來的方法進行獲取
}
}
}
/**
* 觀察者的方法,實現註冊
* 本質上和直接使用Subject在程序中傳遞一個觀察者過去是一樣的
*/
public void register(){
subject.register(this);
}
/**
* 觀察者的方法,實現取消註冊
*/
public void remove(){
subject.remove(this);
}
}
下面我們再來編寫一個測試代碼
package demo;
public class main {
public static void main(String[] args) {
// TODO Auto-generated method stub
//新建一個新聞服務
Subject subject = new Subject();
//新建用戶,觀察者
Observer observer1 = new Observer("小王",subject);
Observer observer2 = new Observer("小黃",subject);
Observer observer3 = new Observer("小陳",subject);
Observer observer4 = new Observer("小吳",subject);
/**
* 用戶註冊,也就是訂閱了新聞服務
*/
observer1.register();
observer2.register();
observer3.register();
observer4.register();
//更新新聞,然後主題(被觀察者)告訴所有註冊了的用戶
subject.updateNews("你好,從你的全世界路過上映了!");
System.out.println("A day later\n");
//小王和小黃取消了關注
observer1.remove();
observer2.remove();
System.out.println("小王和小黃取消了訂閱,將不會在收到新的新聞了\n");
subject.updateNews("你好,火鍋英雄上映啦!!!");
}
}
結果如圖:
這就是自定義的觀察者模式。