码迷,mamicode.com
首页 > 其他好文 > 详细

观察者(Observer)模式

时间:2019-08-16 01:04:39      阅读:123      评论:0      收藏:0      [点我收藏+]

标签:角色   bst   turn   java   接口   就是   zed   建立   cli   

  观察者模式又叫做发布-订阅模式(Publish.Subscribe)模式、模型-视图模式(Model/View)模式、源-监听器模式(Source/Listener)模式或从属者(Dependents)模式。

  观察者模式定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

 1.  观察者模式结构

 一个简单的观察者模型如下:

技术图片

 

角色:

抽象(Subject)主题角色:把所有的观察者维持在一个集合中,每个主题都可以有任意数量的观察者。提供一个接口,可以增加和删除观察者,主题角色又叫做被观察者(Observable)。

抽象观察者(Observer)角色:在得到主题的通知时更新自己。有时候观察者依赖于被观察者,可以将update方法修改为 void update(Subject subject)。

具体主题角色:维护所有的观察者,在具体主题的内部状态改变时给所有登记的观察者发送通知。

具体观察者角色:存储与主题的状态自恰的状态,也就是随着主题的状态改变自己的状态。

 

代码如下:

package cn.qlq.observer;

public interface Subject {
    void attach(Observer observer);

    void delete(Observer observer);

    void notifyObservers();
}

 

package cn.qlq.observer;

import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

public class ConcreteSubject implements Subject {

    private List<Observer> observers = new Vector<>();

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void delete(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }

}

 

package cn.qlq.observer;

public interface Observer {

    /**
     * 
     */
    void update();

}

 

package cn.qlq.observer;

public class ConcreteObserver implements Observer {

    @Override
    public void update() {
        System.out.println(" i am notified");
    }

}

 

2.  第二种实现

  考虑上面的主题中,管理维护观察者集合的方法可以放到抽象类中去实现,因此可以将维护观察者关系的代码抽取到抽象类中,类图如下:

技术图片

  这种方式与上面的区别是代表存储观察者对象的集合从连线是从抽象主题到抽象观察者。(也就是抽象主体维护抽象观察者的引用关系)

 

代码如下:

package cn.qlq.observer;

import java.util.List;
import java.util.Vector;

public abstract class Subject {
    private List<Observer> observers = new Vector<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void delete(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

 

package cn.qlq.observer;

public class ConcreteSubject extends Subject {

    private String state;

    public void changeState(String newState) {
        state = newState;
        this.notifyObservers();
    }

}

 

package cn.qlq.observer;

public interface Observer {

    /**
     * 
     */
    void update();

}

 

package cn.qlq.observer;

public class ConcreteObserver implements Observer {

    @Override
    public void update() {
        System.out.println(" i am notified");
    }

}

 

客户端测试代码:

package cn.qlq.observer;

public class Client {

    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        Observer observer = new ConcreteObserver();

        subject.attach(observer);

        subject.changeState("1");
    }

}

 

3.  Java语言对观察者模式的支持

  在Java.util保重中,提供了一个Observable类以及一个Observer接口。

Observer接口:  此接口只定义了一个update方法,当被观察者的状态发生变化时被观察者对象的notifyOeservers()方法会调用这一方法。

public interface Observer {

    void update(Observable o, Object arg);
}

 

Observable类:被观察者类都是该类的子类,该类有两个重要的方法:

  setChanged():  设置一个内部标记标记其状态发生变化

  notifyObsers(): 这个方法被调用时会调用所有注册的观察者的update()方法。

package java.util;

public class Observable {
    private boolean changed = false;
    private Vector 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) { 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(); } }

 

简单的使用Java对观察者模式的支持:

package cn.qlq.observer;

import java.util.Observable;

public class Watched extends Observable {

    private String data = "";

    public String getData() {
        return data;
    }

    public void changeData(String data) {
        if (!this.data.equals(data)) {
            this.data = data;
            setChanged();
        }

        notifyObservers();
    }

}

 

package cn.qlq.observer;

import java.util.Observable;
import java.util.Observer;

public class Watcher implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        if (o != null && (o instanceof Watched)) {
            Watched watched = (Watched) o;
            System.out.println("data changed to: " + watched.getData());
        }
    }

}

 

客户端代码

package cn.qlq.observer;

public class Client {

    public static void main(String[] args) {
        Watched watched = new Watched();
        Watcher watcher = new Watcher();

        watched.addObserver(watcher);

        watched.changeData("123");
        watched.changeData("123");
        watched.changeData("456");
        watched.changeData("789");
    }
}

结果:(虽然改变了四次值,但是有两次一样,查看源码啊在notifyObsers()中会清掉changed的值)

data changed to: 123
data changed to: 456
data changed to: 789

 

4.  观察者模式优缺点

优点:

(1)观察者模式在被观察者和观察者直接建立一个抽象的耦合

(2)观察者模式支持广播通信。被观察者会向所有登记的观察者发出通知。

缺点:

(1)如果被观察者的观察者过多,通知所有观察者需要花费很多时间

(2)如果在被观察者之间有循环依赖容易循环调用

(3)如果对观察者的通知是通过多线程通知必须保证通知的正确性

(4)观察者可以知道观察者状态发生了变化,不知道是怎么发生变化的。

 

观察者(Observer)模式

标签:角色   bst   turn   java   接口   就是   zed   建立   cli   

原文地址:https://www.cnblogs.com/qlqwjy/p/11324341.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!