观察者模式

观察者模式(Observer Pattern)

别名: 依赖,发布/订阅(Another Name: Dependents, Publish/Subscribe)

概念

定义对象间的一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都得到通知并被自动更新。

如何使用?

当一个对象的数据更新时,需要通知其他对象,而又不希望和被通知的对象形成紧耦合时

示例

比如我们有个天气服务(主题),然后有多个使用它的客户端(观察者),包括android和iphone端app的服务(观察者),那么就可以使用这么模式。

我们需要一种结构存放天气信息(注意,省略了get、set方法!):

//天气的消息实体

public class WeatherInfo {

    private long time;

    private String weather;

    public WeatherInfo(long time,String weather){

        this.time = time;

        this.weather = weather;

    }

    @Override

    public boolean equals(Object obj) {

        WeatherInfo info = (WeatherInfo) obj;

        return info.time==this.time&&info.weather.equals(this.weather);

    }

}

然后我们定义天气服务的接口(主题),以表示它应实现哪些功能:

//主题

public interface IWeatherService {

    void addClient(Client client);      //添加观察者

    boolean deleteClient(Client client);//删除观察者

    void notifyClients();               //通知

    void updateWeather(WeatherInfo info);//主题内容更新

}

接着就是客户端的接口描述:

//观察者

public interface Client {

    void getWeather(WeatherInfo info);

}

然后实现具体的天气服务,这里同样用到了单例模式:

//具体主题

public enum WeatherService implements IWeatherService{

    instance;

    private LinkedList<WeatherInfo> weatherInfos = new LinkedList<WeatherInfo>();

    private LinkedHashSet<Client> clients = new LinkedHashSet<Client>();    //存放观察者

    //添加观察者

    @Override

    public void addClient(Client client) {

        clients.add(client);

    }

    //删除观察者

    @Override

    public boolean deleteClient(Client client) {

        return clients.remove(client);

    }

    //通知观察者

    @Override

    public void notifyClients() {

        Iterator<Client> iterator = clients.iterator();

        while(iterator.hasNext()){

            iterator.next().getWeather(weatherInfos.peekFirst());

        }

    }

    //更新天气

    @Override

    public void updateWeather(WeatherInfo info) {

        if(weatherInfos.size()>0)

            if(weatherInfos.peekFirst().equals(info)) return;

        weatherInfos.push(info);

        if(clients.size()==0) return;

        notifyClients();

    }

}

最后就是具体的客户端(观察者,此处给出两个):

public class ClientAndroidServer implements Client {

    private static String name = "安卓服务";

    private WeatherInfo info;

    @Override

    public void getWeather(WeatherInfo info) {

        this.info = info;

        dealMsg();

    }

    private void dealMsg(){

        System.out.println(name + "收到最新天气:time="+info.getTime()+"msg="+info.getWeather()+"。马上开始推送消息...");

    }

}

public class ClientIphoneServer implements Client {

    private static String name = "苹果服务";

    private WeatherInfo info;

    @Override

    public void getWeather(WeatherInfo info) {

        this.info = info;

        dealMsg();

    }

    private void dealMsg(){

        System.out.println(name + "收到最新天气:time="+info.getTime()+"msg="+info.getWeather()+"。马上开始推送消息...");

    }

}


好,现在就可以直接使用了:

public class TestUse {

    public static void main(String args[]){

        //创建主题

        WeatherService service = WeatherService.instance;

        //添加观察者

        service.addClient(new ClientAndroidServer());

        service.addClient(new ClientIphoneServer());

        //更新主题

        service.updateWeather(new WeatherInfo(System.currentTimeMillis(), "多云"));

        service.updateWeather(new WeatherInfo(System.currentTimeMillis()+10006060*24, "多云转晴"));

        service.updateWeather(new WeatherInfo(System.currentTimeMillis()+10006060242, "晴"));

    }

}

运行后,控制台有如下输出:

安卓服务收到最新天气:time=1461246047007msg=多云。马上开始推送消息…
苹果服务收到最新天气:time=1461246047007msg=多云。马上开始推送消息…
安卓服务收到最新天气:time=1461332447007msg=多云转晴。马上开始推送消息…
苹果服务收到最新天气:time=1461332447007msg=多云转晴。马上开始推送消息…
安卓服务收到最新天气:time=1461418847007msg=晴。马上开始推送消息…
苹果服务收到最新天气:time=1461418847007msg=晴。马上开始推送消息…
    可以看出,观察者模式是一对多的。而本例是将更新的内容整个推给客户端。

而观察者模式中的数据有推和拉的区别,上例是推。

推的方式会将主题更改的内容全部直接推给客户端,拉的方式就是主题的数据更新后,不直接将数据推给客户端,而是先推送一个通知并提供对应的方法供客户端拉取数据。

如果上例中,天气服务每半小时更新(半点和整点推消息),还有一个客户端,不需要特别即时的天气消息,只取整点的消息,那么我们就可以使用拉的方式,数据更新后,给客户端推送一个标志,客户端自己按需取得数据(天气服务需要提供这样一个接口)。这就是拉。

java.util包中也提供了观察者模式的支持,因为java程序设计中使用比较广泛。有一个Observable类(相当于这里的具体主题)和一个Observer接口(相当于这里的主题接口):

public interface Observer {

    void update(Observable o, Object arg);

}

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) {

        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的API。 但可以看到里面还在使用Vector(已过时),这其实是不推荐的,我们可以自己实现观察者模式,如果是多线程中,我们也可以自己实现同步。

23种设计模式:https://blog.csdn.net/anxpp/article/details/51224293

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值