设计模式-观察者模式

144 阅读3分钟

1,通用写法

//抽象主题者
public interface ISubject<E> {
    boolean attach(IObserver<E> observer);
    boolean detach(IObserver<E> observer);
    void notify(E event);
}

//具体主题者
public class ConcreteSubject<E> implements ISubject<E> {
    private List<IObserver<E>> observers = new ArrayList<IObserver<E>>();
    public boolean attach(IObserver<E> observer) {
        return !this.observers.contains(observer) && this.observers.add(observer);
    }
    public boolean detach(IObserver<E> observer) {
        return this.observers.remove(observer);
    }
    public void notify(E event) {
        for (IObserver<E> observer : this.observers) {
            observer.update(event);
        }
    }
}

//抽象观察者
public interface IObserver<E> {
    void update(E event);
}

//具体观察者
public class ConcreteObserver<E> implements IObserver<E> {
    public void update(E event) {
        System.out.println("receive event: " + event);
    }
}

public class Test {
    public static void main(String[] args) {
        // 被观察者
        ISubject<String> observable = new ConcreteSubject<String>();
        // 观察者
        IObserver<String> observer = new ConcreteObserver<String>();
        // 注册
        observable.attach(observer);
        // 通知
        observable.notify("hello");
    }
}

看一下类图:

我们可以看到观察者模式的几种角色:

抽象主题(subject):指被观察的对象(Observable)。该角色是一个抽象类或接口,定义了增加,删除,通知观察者的方法。
具体主题(ConcreteSubject):具体被观察者,当期内部状态变化时,会通知已注册的观察者。
抽象观察者(Observer):定义了相应通知的更新方法。
具体观察者(ConcreteObserver):在得到状态更新时,会自动做出响应。

下面举一个业务场景,主要是用到了jdk中的观察者的抽象

Observable和Observer都是JDK中的

public class GPer extends Observable {
    private String name = "GPer生态圈";
    private static final GPer gper = new GPer();
    private GPer() {}
    public static GPer getInstance(){
        return gper;
    }
    public String getName() {
        return name;
    }
    public void publishQuestion(Question question){
        System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。");
        setChanged();
        notifyObservers(question);
    }
}

public class Teacher implements Observer {
    private String name;
    public Teacher(String name) {
        this.name = name;
    }
    public void update(Observable o, Object arg) {
        GPer gper = (GPer)o;
        Question question = (Question)arg;
        System.out.println("======================");
        System.out.println(name + "老师,你好!\n" +
                        "您收到了一个来自" + gper.getName() + "的提问,希望您解答。问题内容如下:\n" +
                        question.getContent() + "\n" +
                        "提问者:" + question.getUserName());
    }
}

@Data
public class Question {
    private String userName;
    private String content;
}

public class Test {
    public static void main(String[] args) {
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("Tom");
        Teacher jerry = new Teacher("Jerry");
        gper.addObserver(tom);
        gper.addObserver(jerry);

        //用户行为
        Question question = new Question();
        question.setUserName("张三");
        question.setContent("观察者模式适用于哪些场景?");
        
        gper.publishQuestion(question);
    }
}

------------控制台打印如下-----------
张三在GPer生态圈上提交了一个问题。
======================
Jerry老师,你好!
您收到了一个来自GPer生态圈的提问,希望您解答。问题内容如下:
观察者模式适用于哪些场景?
提问者:张三
======================
Tom老师,你好!
您收到了一个来自GPer生态圈的提问,希望您解答。问题内容如下:
观察者模式适用于哪些场景?
提问者:张三

此示例的类图如下:

观察者模式优缺点:

优点:
1,观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则。
2,分离了表现层(观察者)和数据逻辑层(被观察者),并且建立一套触发机制,使得数据的变化可以影响到多个表现层上。
3,实现一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。

缺点:
1,如果观察者数量过多,则事件通知耗时较长。
2,事件通知呈线性关系,如果一个观察者处理时间卡壳,会影响到后面的观察者接收到该事件。
3,如果观察者和被观察者存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。