设计模式第三天之自定义观察者模式

在上一边文章中,和大家分享了基于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 }

运行的结果是这样的:

posted @ 2016-11-19 21:52  于王令  阅读(85)  评论(0)    收藏  举报