设计模式--观察者模式初探和java Observable模式

摘要:
实现观察者接口,然后调用Observable对象的addObserver()方法。当你不再想成为观察员时,只需致电deleteObserver()被观察者(受试者)如何发送通知?步骤1:调用setChanged()方法以确定状态已更改的事实步骤2:调用notifyObserver()方法或notifyobserver,这涉及推送和拉取数据传输如果您想在推送模式下将数据“推送”给观察者,可以将数据作为数据对象发送给notifyObse
初步认识观察者模式

  观察者模式又称为发布/订阅(Publish/Subscribe)模式,因此我们可以用报纸期刊的订阅来形象的说明:

    报社方负责出版报纸.

    你订阅了该报社的报纸,那么只要报社发布了新报纸,就会通知你,或发到你手上.

    如果你不想再读报纸,可以取消订阅,这样,报社发布了新报纸就不会再通知你.

  理解其实以上的概念,就可以理解观察者模式,观察者模式中有主题(Subject)和观察者(Observer),分别对应报社和订阅用户(你).观察者模式定义了对象之间的一对多的依赖关系,这样,当"一"的一方状态发生变化时,它所依赖的"多"的一方都会收到通知并且自动更新.如图:

      设计模式--观察者模式初探和java Observable模式第1张

实现观察者模式

  这里以师生关系为例,老师和学生是一对多的关系,老师给学生布置作业,这个动作作为主题事件,每当老师布置一道题时,就要自动通知到所有的学生把该题记下来,然后再布置下一道题...

设计模式--观察者模式初探和java Observable模式第2张

  这是一个并不标准的类图,主题接口和实现类中,一般需要有addObserver(),deleteObserver(),notifyObserver();用来注册删除观察者以及在主题状态发生变化时通知所有的观察者对象.

  接下来进行代码实现:

  Subject接口:

package com.wang.observer;
//主题接口
 interface Subject {
     //添加观察者
     void addObserver(Observer obj);
     //移除观察者
     void deleteObserver(Observer obj);
     //当主题方法改变时,这个方法被调用,通知所有的观察者
     void notifyObserver();
}

  Oserver接口:

package com.wang.observer;

interface Observer {
    //当主题状态改变时,会将一个String类型字符传入该方法的参数,每个观察者都需要实现该方法
    public void update(String info);
}

Subject接口实现类TeacherSubject:

package com.wang.observer;

import java.util.ArrayList;
import java.util.List;

public class TeacherSubject implements Subject {
    //用来存放和记录观察者
    private List<Observer> observers=new ArrayList<Observer>();
    //记录状态的字符串
    private String info;
    
    @Override
    public void addObserver(Observer obj) {
        observers.add(obj);
    }

    @Override
    public void deleteObserver(Observer obj) {
        int i = observers.indexOf(obj);
        if(i>=0){
            observers.remove(obj);
        }
    }

    @Override
    public void notifyObserver() {
        for(int i=0;i<observers.size();i++){
            Observer o=(Observer)observers.get(i);
            o.update(info);
        }
    }
    //布置作业的方法,在方法最后,需要调用notifyObserver()方法,通知所有观察者更新状态
    public void setHomework(String info){
        this.info=info;
        System.out.println("今天的作业是"+info);
        this.notifyObserver();
    }

}

Observer接口实现类StudentObserver:

package com.wang.observer;

public class StudentObserver implements Observer {

    //保存一个Subject的引用,以后如果可以想取消订阅,有了这个引用会比较方便
    private TeacherSubject t;
    //学生的姓名,用来标识不同的学生对象
    private String name;
    //构造器用来注册观察者
    public Student(String name,Teacher t) {
        this.name=name;
        this.t = t;
        //每新建一个学生对象,默认添加到观察者的行列
        t.addObserver(this);
    }


    @Override
    public void update(String info) {
        System.out.println(name+"得到作业:"+info);
        
    }

}

测试类TestObserver:

package com.wang.observer;

public class TestObserver {

    public static void main(String[] args) {
        
        TeacherSubject teacher=new TeacherSubject();
        StudentObserver zhangSan=new StudentObserver("张三", teacher);
        StudentObserver LiSi=new StudentObserver("李四", teacher);
        StudentObserver WangWu=new StudentObserver("王五", teacher);
        
        teacher.setHomework("第二页第六题");
        teacher.setHomework("第三页第七题");
        teacher.setHomework("第五页第八题");
    }
}

打印结果:

今天的作业是第二页第六题
张三得到作业:第二页第六题
李四得到作业:第二页第六题
王五得到作业:第二页第六题
今天的作业是第三页第七题
张三得到作业:第三页第七题
李四得到作业:第三页第七题
王五得到作业:第三页第七题
今天的作业是第五页第八题
张三得到作业:第五页第八题
李四得到作业:第五页第八题
王五得到作业:第五页第八题

从打印结果看,每当老师布置作业的状态改变,就会通知每一个学生.以上就是一个简单的观察者模式的实现.

java内置的观察者模式:

  在java.util包中包含有基本的Observer接口和Observable抽象类.功能上和Subject接口和Observer接口类似.不过在使用上,就方便多了,因为许多功能比如说注册,删除,通知观察者的那些功能已经内置好了.

  使用javaAPI的观察者模式需要明白这么几件事情:

   如何使对象变为观察者?

    实现观察者接口(java.util.Observer),然后调用Observable对象的addObserver()方法.不想再当观察者时,调用deleteObserver()就可以了.

   被观察者(主题)如何发出通知?

    第一步:先调用setChanged()方法,标识状态已经改变的事实.

    第二步:调用notifyObservers()方法或者notifyObservers(Object arg),这就牵扯到推(push)和拉(pull)的方式传送数据.如果想用push的方式"推"数据给观察者,可以把数据当做数据对象传送给notifyObservers(Object arg)方法,其中的arg可以为任意对象,意思是你可以将任意对象传送给每一个观察者.如果调用不带参数的notifyObserver()方法,则意味着你要使用pull的方式去主题对象中"拉"来所需要的数据.

   观察者如何接收通知?

     观察者只需要实现一个update(Observable o,Object arg)方法,第一个参数o,是指定通知是由哪个主题下达的,第二个参数arg就是上面notifyObserver(Object arg)里传入的数据,如果不传该值,arg为null.

  下面使用java内置API实现上面我所写的老师和学生的例子:

被观察者TeacherSubject:

package com.wang.observer1;

import java.util.Observable;

public class Teacher extends Observable {
        //布置作业的状态信息字符串
    private String info;
    public void setHomework(String info) {

        this.info=info;
        System.out.println("布置的作业是"+info);

        setChanged();
        notifyObservers();
    }
    public String getInfo() {
        return info;
    }
}

观察者StudentObserver:

package com.wang.observer1;

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

public class Student implements Observer{

    private Observable ob;
    private String name;

    public Student(String name,Observable ob) {
        this.ob = ob;
        this.name=name;
        ob.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        Teacher t=(Teacher)o;
        System.out.println(name+"得到作业信息:"+t.getInfo());
        
    }

}

  测试代码和打印结果我就不再写了,和上面的例子是一样一样的,在这个例子中我使用的是"pull"的方式拉数据,在需要传递状态的TeacherSubject中定义了一个info字符串的get方法,在观察者对象中调用get方法得到所需数据,如果希望使用push的方式,只需要在TeacherSubject类的notifyOservers()方法中传入String类型的info字符串即可在update()方法中直接通过第二个参数获取到arg,即使前面传过来的info字符串.

 观察者模式的好处

  观察者模式提供了一种对象设计,让主题和观察者之间耦合度降得很低,为什么呢?关于观察者的一切,主题只知道观察者实现了Observer接口,并不需要观察者具体的类是谁,做了什么或者其他细节.

  这样的话,由于松耦合,改变主题或者观察者其中一方,并不会影响另一方,只要他们之间的接口仍被遵守,就可以自由地改变它.

  降低对象之间的耦合度,也是面设对象设计的一个很重要的原则.  

 

免责声明:文章转载自《设计模式--观察者模式初探和java Observable模式》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇input的相关兼容性问题iOS 测试 | iOS 自动化性能采集下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

thinkphp获取ip地址及位置信息

ThinkPHPCommonfuncitons.php下有一个get_client_ip()能够获取ip地址 但是有时候不够准确 找到了下面一段来 function get_client_ip($type = 0) { $type = $type ? 1 : 0; static $ip = NUL...

将两个ListMap中同下标的map去重合并

public static void main(String[] args) { Map<String,Object> oneMap = new HashMap<>(); oneMap.put("key","001"); oneMap.put("name","张飞")...

golang下使用ini配置文件(widuu/goini)

在“widuu/goini”基础上进行了修改,增加了其他数据类型配置值(string、int、int32、int64、[]int、[]string)的支持。 使用方法: ConfigCentor := goini.SetConfig("./config.ini") 读取int配置值:ConfigCentor.GetValueInt("ES","LogLev...

WebBench简介

一:简介:          Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。Webbench使用C语言编写, 代码加起来不到600行。          http://home.tiscali.cz/~cz2...

Linux学习: LCD驱动

一、LCD驱动框架: 1.分配一个fb_info结构体:s3c_lcd = framebuffer_alloc(0,NULL); 2.设置fb_info(s3c_lcd): ID、固定参数、可变参数、设置操作函数 及 其他设置 3.注册:register_framebuffer(s3c_lcd); 4.硬件相关的操作:   ①分配引脚用于LCD      ...

Java—RequestMapping相关用法

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。它有6个属性:1.value:指定请求的具体地址:value的uri值为以下三类:A) 可以指定为普通的具体值:(value = "/add"),其直接访问controller的路径是ip:port/member/a...