在上一边文章中,和大家分享了基于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("你好,火锅英雄上映啦!!!");
}
}
结果如图:
这就是自定义的观察者模式。