观察者模式

73 阅读4分钟

理解

当我们在打团队游戏时,当你受到攻击需要队友帮忙时该怎么办?
这时候就需要给你所有的队友发送一条你正在被攻击的消息。所有的队友会根据你发送的消息作出相应的动作。比如有团队意识来帮你,或者不帮你继续玩自己的。
这里面的队员就是该设计模式名字中的观察者。那么受到攻击的自己的是什么呢。被观察者?不,准确的我们称之为目标或者主题。
所以整个流程大概就是:当目标(主题)的状态发生改变时就会通知观察者,观察者根据自己的情况做出相应的动作。

观察者模式主要角色组成

抽象主题角色

把所有观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者角色,一般用一个抽象类或接口来实现。

抽象观察者角色

为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。

具体主题角色

在具体主题内部状态改变时,给所有登记过的观察者发出通知,具体主题角色通常用一个子类实现。

具体观察者角色

该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调,如果需要,具体观察者角色可以有一个指向具体主题角色的引用,通常用一个子类实现

例子

/**
 * 抽象观察者
 */
public interface AbstractObserver {
    public void update();
}
/**
 * 抽象主题
 */
public interface AbstractSubject {
    public void addObserver(AbstractObserver observer);
    public void removeObserver(AbstractObserver observer);
    public void notification();
}
/**
 * 具体主题
 */
public class ConcreteSubject implements AbstractSubject{

   private List<AbstractObserver> observers=new ArrayList<>();

    @Override
    public void addObserver(AbstractObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(AbstractObserver observer) {
        observers.remove(observer);
    }

    @Override
    public void notification() {
        for (AbstractObserver observer:observers) {
                observer.update();
        }
    }
}

测试类

public static void main(String[] args) {

        AbstractSubject subject=new ConcreteSubject();

        subject.addObserver(new AbstractObserver() {
            @Override
            public void update() {
                System.out.println("A同学去上课了!");
            }
        });

        subject.addObserver(new AbstractObserver() {
            @Override
            public void update() {
                System.out.println("B同学谈恋爱了!");
            }
        });

        subject.addObserver(new AbstractObserver() {
            @Override
            public void update() {
                System.out.println("C同学被叫到办公室了!");
            }
        });

        subject.notification();
}

关键代码就是通过一个ArrayList保存观察者。

Java内置的观察者模式框架

java内置观察者模式框架提供了类Observable与接口Observer:

类Observable对应抽象主题角色,内部维护Vector集合来存储具体观察者角色,接口Observer对应抽象观察者角色。

//具体主题角色
public class Watched extends Observable {
    //状态改变的时候调用已注册的观察者的update方法,让它们更新自己
    public void count(int number) {
        for (; number >= 0; number--) {
            try {
                Thread.sleep(1000);
                //告知变更
                setChanged();     
                //通知所有与我相关的Observer
                notifyObservers(number);  
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
//具体观察者角色:
public class Watcher implements Observer {
    //当主题角色事件触发时,会调用所有已注册的具体观察者角色的update方法
    @Override
    public void update(Observable o, Object arg) {
        int number = (Integer) arg;
        System.out.println(number);
    }
}

测试类:

public static void main(String[] args) {
        #创建主题角色
        Watched watched = new Watched();
        #创建观察者角色
        Observer watcher1 = new Watcher();
        #自实现
        Observer watcher2 = new Observer() {
            @Override
            public void update(Observable o, Object arg) {
                int number = (Integer) arg;
                if (0 == number) {
                    System.out.println("done");
                }
            }
        };
        watched.addObserver(watcher1);
        watched.addObserver(watcher2);
        watched.count(10);
}

注意:

  • 被观察者要继承Observable类
  • 被观察者通知观察者时,也就是调用notifyObservers方法时一定要先调用setChanged()方法,该方法作用是将对象里面的changed这个boolean变量设为true,因为notifyObservers要首先检查该变量是否为true,如果为false就不执行而直接返回了。

扩展

Observer和Observable在Java 9标记为废弃,Observer和Observable有几个原因:

  • 不能序列化

Observable没有实现Serializable接口,它的内部成员变量都是私有的,子类不能通过继承它来对Observable的成员变量处理。所以子类也不能序列化。

  • 不是线程安全

在 java.util.Observable文档里没有强制要求Observable是线程安全的,它允许子类覆盖重写Observable的方法,事件通知无序以及事件通知发生在不同的线程里,这些都是会影响线程安全的问题。

  • 支持事件模型的功能简单

例如,只是支持事情发生变化的概念,但是不能提供更多哪些内容发生了改变。

替代

可以使用java.beans 里的PropertyChangeEvent 和 PropertyChangeListener 来代替目前Observer和Observable的功能。