Java观察者模式

先来领会一下观察者模式的精神:

观察者模式定义了一系列对象之间一对多的关系。通俗的比喻相当于报社和订报的人,水电局和用户。也就是出版者和订阅者。

观察者模式涉及到了如下几点定义:

  抽象主题角色

  具体主题角色

  抽象观察者角色

  具体观察者角色

1.接下来是具体的例子

就拿某直播平台的通知功能来说,其类图如下:(Tuhao就是土豪)

Java观察者模式

假设游戏是一个大的主题,在这里它相当于一个抽象主题角色:

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分别为抽象主题角色以及抽象观察者角色。

使用它们改写观察者的关系如下:

Java观察者模式

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 设计模式