先来领会一下观察者模式的精神:
观察者模式定义了一系列对象之间一对多的关系。通俗的比喻相当于报社和订报的人,水电局和用户。也就是出版者和订阅者。
观察者模式涉及到了如下几点定义:
抽象主题角色
具体主题角色
抽象观察者角色
具体观察者角色
1.接下来是具体的例子
就拿某直播平台的通知功能来说,其类图如下:(Tuhao就是土豪)
假设游戏是一个大的主题,在这里它相当于一个抽象主题角色:
public interface Game { void addPeople(People people); void removePeople(People people); void notifyPeople(); }
抽象主题角色最基本具有添加,移除以及通知观察者的功能
然后是具体主题角色,例如在游戏分类下的StarCraft就是其中的一个游戏(具体主题角色):
public class StarCraft implements Game { private ArrayList<People> viewer; private String message; public StarCraft() { viewer = new ArrayList<People>(); } public void addPeople(People people) { viewer.add(people); } public void removePeople(People people) { if (viewer.contains(people)) { viewer.remove(people); } else { System.out.println("No viewers!"); } } public void setMessage(String message) { this.message = message; } public void notifyPeople() { for (People people : viewer) { people.update(message); } } }
它维护着该主题下的用户(观察者)列表,负责移除,添加观察者,发送通知等一系列操作。
接下来就是抽象观察者角色了:
public interface People { void update(String message); }
最后就是具体观察者角色,例如上斗鱼看直播的土豪:
public class Tuhao implements People { public void update(String message) { System.out.println("Watch: " + message+" and send money!"); } }
最后就是测试了:
斗鱼某主播上开了直播,然后系统会通知订阅该直播的人直播开始了。
测试:
@Test public void test() { StarCraft starCraft = new StarCraft(); starCraft.setMessage("干死黄旭东!"); //土豪mike Tuhao mike = new Tuhao(); starCraft.addPeople(mike); starCraft.notifyPeople(); } //Watch: 干死黄旭东! and send money!
2.在Java JDK中,自带有java.util.Observable以及Observer分别为抽象主题角色以及抽象观察者角色。
使用它们改写观察者的关系如下:
Observable默认使用Vector保存期观察者,且对其中的大部分方法都加了synchronized进行同步。不过,Observable是个类,继承了它就不能继承其他的类了。
此外,还增加了changed标志,只有当changed标志为true的时候,通知才可进行,且通知完成后,会将标志重置为false:
其代码如下:
public class Observable { private boolean changed = false; private Vector<Observer> obs; public Observable() { obs = new Vector<>(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
具体主题:
public class StarCraft extends Observable { public void setChanged(){//Observable的setChanged为protected,必须在主题实现中实现 super.setChanged(); } }
具体观察者:
public class Tuhao implements Observer{ public void update(Observable o, Object arg) { System.out.println("土豪 online!"); if(arg!=null){ System.out.println(arg.toString()); } } }
测试:
@Test public void test1(){ StarCraft starCraft = new StarCraft(); Tuhao mike = new Tuhao(); starCraft.addObserver(mike); //可以查看状态是否改变 boolean status = starCraft.hasChanged(); System.out.println("状态是否改变:"+status); starCraft.notifyObservers(); //可以查看共有多少待通知的对象 System.out.println("将通知人数为:"+starCraft.countObservers()); //改变状态,这里是重写了Observable的setChanged方法 starCraft.setChanged(); status = starCraft.hasChanged(); System.out.println("状态是否改变:"+status); starCraft.notifyObservers(); System.out.println("通知人数为:"+starCraft.countObservers()); //通知完之后,状态会恢复原样,即为false,等待下一次需要状态的改变 status = starCraft.hasChanged(); System.out.println("状态是否改变:"+status); } // 状态是否改变:false // 将通知人数为:1 // 状态是否改变:true // 土豪 online! // 通知人数为:1 // 状态是否改变:false
此外,还可以调用:
starCraft.notifyObservers("额外信息");
方法传递额外的信息。
3.最后,在Java GUI中,监听器的添加使用的就是观察者模式:
JButton j = new JButton(); j.addActionListener(new AbstractAction() { public void actionPerformed(ActionEvent e) { System.out.println("got it!"); } });
监听器的实现类(上面的匿名内部类)就是观察者,JButton就是主题。
如上:)
参考资料:
Head First 设计模式