設計模式第三天-親力親爲實現觀察者模式

在上一邊文章中,和大家分享了基於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("你好,火鍋英雄上映啦!!!");
	}
}

結果如圖:

這就是自定義的觀察者模式。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章