设计模式第三天之自定义观察者模式
在上一边文章中,和大家分享了基于java内置实现的观察者模式。在本次分享中呢,将会是一个完全自己定义的一个观察者模式。我们还是以新闻报社为例子。
在这里,我们将报社(被观察者)成为主题(Subject),观察者成为(Observer),在第一篇文章中,我们讨论了策略模式,也就是我们要针对接口编程,所以我们首先来定义两个接口,一个是主题接口,另一个是观察者接口。
主题接口有什么内容呢?首先我们应该允许增加新的注册用户(订阅报社的用户),还有用户进行取消注册,还有就是当有新的数据的时候,应该能够通知所有的注册了的用户。因此应该有三个方法。
1 package demo; 2 /** 3 * 观察者模式的主题接口 4 * @author wangpeiyu 5 * 6 */ 7 public interface ISubject { 8 public void register(IObserver observer); 9 public void remove(IObserver subject); 10 public void notifys(); 11 }
观察者接口应该有什么呢?首先最重要的就是更新相关的数据了。
更新有两个方法:
方法一:直接通过update方法,将所有的数据以参数的形式传递过来
方法二: 直接将持有数据的主题(可观察者,也就是报社)的对象传递过来,观察者自己通过该对象get想要的数据。
1 package demo; 2 /** 3 * 观察者模式的观察者接口 4 * @author wangpeiyu 5 * 6 */ 7 public interface IObserver { 8 public void update(String news); 9 public void update(ISubject subject); 10 }
下面我们来编写主题(可观察者)的实现类。
除了要有接口的注册、取消注册和通知所有用户的方法外,还要什么方法呢?
我们肯定要有一个更新数据然后触发通知所有用户方法的方法,这里我们就用updateNews(),更新新闻的方法,来表示数据发生了改变,要通知相关的用户了。
1 package demo; 2 import java.util.ArrayList; 3 public class Subject implements ISubject { 4 //维护观察者的数组,可以通过遍历整个数组,达到通知所有注册了的用户 5 private ArrayList<IObserver> observerList; 6 //新闻信息 7 private String news; 8 9 public Subject() { 10 // TODO Auto-generated constructor stub 11 observerList = new ArrayList<IObserver>(); 12 news=""; 13 } 14 public void updateNews(String news){ 15 this.news=news; 16 notifys();//更新了新闻之后,应该通知所有的一已经注册了的用户,即观察者 17 } 18 /** 19 * 返回新的新闻信息 20 * 当在notifys中使用的是第一种传递方式的时候,可以使用这个方法,但是,此方法必须要设置为public 21 * 该方法的好处在于,用户可以自定义的获取自己想要的信息 22 * 但是将本主题对象传递过去,容易暴露信息 23 * 如果使用的是第二种方式的时候,可以直接将新的新闻信息传递出去,此时getNews这个方法可以是私有的 24 * 这个方法的好处在于,只将所有数据传递出去,不容易暴露主题对象的信息 25 * 不足是全部信息都传递出去,有些是用户不需要的 26 * 27 * @return 28 */ 29 public String getNews(){ 30 return news; 31 } 32 33 @Override 34 public void notifys() { 35 // TODO Auto-generated method stub 36 for(IObserver observer:observerList) 37 { 38 observer.update(this);//这是一种传递方式 39 observer.update(news);//这是另一种方式 40 } 41 } 42 /** 43 * 注册观察者 44 */ 45 @Override 46 public void register(IObserver observer) { 47 // TODO Auto-generated method stub 48 if(!observerList.contains(observer)) 49 { 50 observerList.add(observer); 51 } 52 } 53 /** 54 * 观察者取消注册 55 */ 56 @Override 57 public void remove(IObserver observer) { 58 // TODO Auto-generated method stub 59 //找到要取消注册的观察者的索引 60 //如果索引为-1,则表示该观察者还没有注册,所以不用进行取消注册 61 //否则就是已经注册了,然后进行取消 62 int index = observerList.indexOf(observer); 63 if(index!=-1) 64 { 65 observerList.remove(index); 66 } 67 } 68 }
接着我们再来实现观察者的方法。方法注释已经写得比较详细了。大家可以看代码块
1 package demo; 2 3 public class Observer implements IObserver { 4 private String name; 5 private ISubject subject; 6 /** 7 * 为什么要传递Subject对象过来呢???? 8 * 大家可以思考。 9 * 我个人觉得,传递Subject对象过来,可以直接在观察者中增加注册和取消注册的方法,然后在方法里面调用Subject进行注册 10 * 这样的好处是,可以直接通过观察者就可以完成了自身的注册和取消注册 11 * 可以避免要注册的时候,直接使用Subject类注册再传递一个观察者过去,维护性不强 12 * @param name 13 * @param subject 14 */ 15 public Observer(String name,ISubject subject) { 16 // TODO Auto-generated constructor stub 17 this.name = name; 18 this.subject = subject; 19 } 20 @Override 21 public void update(String news) { 22 // TODO Auto-generated method stub 23 System.out.println(name+" "+news+"\n");//直接将传递过来的,自己感兴趣的数据进行输出 24 25 } 26 @Override 27 public void update(ISubject subject) { 28 // TODO Auto-generated method stub 29 if(subject!=null) 30 { 31 if(subject instanceof Subject) 32 { 33 Subject subjectEntires = (Subject)subject; 34 System.out.println(name+" "+subjectEntires.getNews()+"\n");//这时候通过暴露出来的方法进行获取 35 } 36 } 37 } 38 /** 39 * 观察者的方法,实现注册 40 * 本质上和直接使用Subject在程序中传递一个观察者过去是一样的 41 */ 42 public void register(){ 43 subject.register(this); 44 } 45 /** 46 * 观察者的方法,实现取消注册 47 */ 48 public void remove(){ 49 subject.remove(this); 50 } 51 }
下面我们再来编写一个测试代码
1 package demo; 2 public class main { 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 //新建一个新闻服务 6 Subject subject = new Subject(); 7 //新建用户,观察者 8 Observer observer1 = new Observer("小王",subject); 9 Observer observer2 = new Observer("小黄",subject); 10 Observer observer3 = new Observer("小陈",subject); 11 Observer observer4 = new Observer("小吴",subject); 12 /** 13 * 用户注册,也就是订阅了新闻服务 14 */ 15 observer1.register(); 16 observer2.register(); 17 observer3.register(); 18 observer4.register(); 19 //更新新闻,然后主题(被观察者)告诉所有注册了的用户 20 subject.updateNews("你好,从你的全世界路过上映了!"); 21 System.out.println("A day later\n"); 22 //小王和小黄取消了关注 23 observer1.remove(); 24 observer2.remove(); 25 System.out.println("小王和小黄取消了订阅,将不会在收到新的新闻了\n"); 26 subject.updateNews("你好,火锅英雄上映啦!!!"); 27 } 28 }
运行的结果是这样的:


浙公网安备 33010602011771号