浅析Java设计模式中的观察者模式

首先,这篇文章是针对于有一定java基础的伙伴,尽请谅解!大家对java设计模式中的观察者模式应该有一定的了解。我也是最近几天对这个模式有了一定的自己的理解。今天,我想将自己书写的这部分代码与代码思想与大家分享,有不足之处,请大家多提意见!
我介绍两种实现方式,一种是自己写的,另外一种是java本身自带的。在写代码之前,我想先来介绍一下什么是观察者模式?以及为什么要引入它?它的利与弊?

  1. 什么是观察者模式?
    答:观察者模式是一个对象与不同于它自己其它对象的一种依赖关系,依赖它的对象可以有多个,当这个被依赖的对象状态改变时,它会通过个别方式通知依赖于它的多个对象。
    以下是百度百科的说法:

在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

2.为什么要引入它?
借用他人的说法

使用观察者模式最大的好处就是减少死循环式的轮循带来的资源无端消耗,并且有着良好的可扩展性。

3.它的利与弊?
这个问题的回答信息来源于http://bianchengzhe.com/DesignPattern/neirong/221.html

> 观察者模式的效果有以下的优点:

第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。

由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。

第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知,

>观察者模式有下面的缺点:

第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。

第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。

第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。

以上对观察者模式进行了一些简单的介绍,接下来我们通过一个生活中简单的例子来进一步理解这个模式。以老师与学生为例,老师负责给学生们布置作业,学生负责接收老师布置的作业信息。此时,学生就是观察者,老师是被观察者,观察者要接收被观察者所发布的消息或者可以说当被观察者的状态有变动时,观察者要及时接收到被观察者新的状态信息,对于这一点,我们写一个接口,这个接口中应该有一个当被观察者状态发生变化时用于更新被观察者的状态方法,而且让观察者继承这个接口,从而达到观察者及时更新被观察者最新状态信息的目的。此时,被观察者要管理多个观察者,简单的管理包括:添加、删除、通知,对于这些简单的操作,我们同样写一个接口,在这个接口中定义这些方法,接下来让具体的被观察者继承这个接口,具体可以看一下图解:
思路描述:
思路描述:

类与类的关系:
这里写图片描述

原理讲完之后,进入代码阶段。首先我们来写供观察者也就是学生所要继承的接口:

package com.mec.about_iobserver;

public interface IStudent {
    //供学生使用更新老师发布作业信息的方法
    void update();
}

接下来,我们写出供被观察者也就是老师所要继承的接口:

package com.mec.about_iobserver;

public interface ITeacher {
    void addStudent(IStudent student);  //添加学生的方法
    void removeStudent(IStudent student);  //移除学生的方法
    void notifyStudent();  //通知学生的方法
    void setChange(); //当信息改变时,所要调用的方法
}

接下来,我们写出具体的老师这个类:

package com.mec.about_observer;
import java.util.ArrayList;
import java.util.List;
import com.mec.about_iobserver.IStudent;
import com.mec.about_iobserver.ITeacher;

public class Teacher implements ITeacher{
    private String homeworkInfo;  //定义一个字符串类型的作业变量
    private List<IStudent> studentList; //定义一个用于老师管理学生的列表

    public Teacher() {
        //出于习惯,在构造方法中实例化这个列表
        studentList = new ArrayList<>();
    }
    //实现ITeacher接口的addStudent(IStudent student)方法
    @Override
    public void addStudent(IStudent student) {
        //要添加学生,则为了避免运行时出现异常,应该在添加之前做一系列的判断
        if(student == null 
                || studentList == null
                || studentList.contains(student)) {
            return;
        }
        studentList.add(student);
    }
    //实现ITeacher接口的removeStudent(IStudent student)方法
    @Override
    public void removeStudent(IStudent student) {
        //要移除学生,则为了避免运行时出现异常,应该在添加之前做一系列的判断
        if(student == null 
                || studentList == null
                || !(studentList.contains(student))) {
            return;
        }
        studentList.remove(student);
    }

    //实现ITeacher接口的notifyStudent()方法
    @Override
    public void notifyStudent() {
        for(IStudent student : studentList) {
            //底下的这一行代码要特别注意,我认为这是连接两个类的重要之处
            student.update();
        }

    }
    //实现ITeacher接口的setChange()方法
    @Override
    public void setChange() {
        System.out.println("当老师布置的作业有变化时输出!");

    }

    public String getHomeworkInfo() {
        return homeworkInfo;
    }
    //设置这个字符串变量
    public void setHomeworkInfo(String homeworkInfo) {
        System.out.println("老师今天布置的作业是:" + homeworkInfo);
        this.homeworkInfo = homeworkInfo;
        //当老师发布的作业信息有改变时会调用这个方法来通知学生
        this.setChange();
        //调用notifyStudent()方法,这个方法在调用完setChange()之后调用
        this.notifyStudent();
    }
}

接下来,我们写出具体的学生这个类:

package com.mec.about_observer;
import com.mec.about_iobserver.IStudent;

public class Student implements IStudent{
    private String name;  //定义一个学生的名字
    private Teacher teacher;  //定义一个Teacher类型的实例
    //这个构造方法中含有参数,其参数的含义是在初始化Student这个类的实例时会默认将name所对应的学生加这个老师所管理的列表中
    public Student(String name,Teacher teacher) {
        this.name = name;
        this.teacher = teacher;
        //将这个学生加入这个老师所对应的列表中
        teacher.addStudent(this);

    }
    //实现IStudent这个接口所对应的update()方法
    @Override
    public void update() {
        System.out.println(name + ":我收到的作业是:" + teacher.getHomeworkInfo());
    }
}

这些类都写完以后,我们来测试这两个实现接口的类 Teacher、Student:

package com.mec.about_observer.test;
import com.mec.about_observer.Student;
import com.mec.about_observer.Teacher;

public class ObserverTest {

    @SuppressWarnings({ "unused" })
    public static void main(String[] args) {
        //定义一个Teacher的实例
        Teacher teacher = new Teacher();
        //定义三个Student类的实例,这里注意构造方法中要添加参数,添加时注意参数类型
        @SuppressWarnings({ })
        Student studentOne = new Student("小小", teacher);
        @SuppressWarnings({ "unused" })
        Student studentTwo = new Student("大大", teacher);
        Student studentThree = new Student("蘑菇", teacher);
        //老师开始布置作业了
        teacher.setHomeworkInfo("将学习的观察者模式自己实现一遍!");
        //老师第一次布置完作业之后,将studentThree从学生列表中移除
        teacher.removeStudent(studentThree);
        System.out.println("这是将蘑菇同学移除后的输出!");
        teacher.setHomeworkInfo("将学习的工厂模式实现一遍!");
    }
}

最后,我们将测试的结果输出一下:

老师今天布置的作业是:将学习的观察者模式自己实现一遍!
当老师布置的作业有变化时输出!
小小:我收到的作业是:将学习的观察者模式自己实现一遍!
大大:我收到的作业是:将学习的观察者模式自己实现一遍!
蘑菇:我收到的作业是:将学习的观察者模式自己实现一遍!
这是将蘑菇同学移除后的输出!
老师今天布置的作业是:将学习的工厂模式实现一遍!
当老师布置的作业有变化时输出!
小小:我收到的作业是:将学习的工厂模式实现一遍!
大大:我收到的作业是:将学习的工厂模式实现一遍!

以上代码是自己实现观察者模式。Java自身还有观察者模式,以下是通过Java自身的接口与抽象方法实现观察者模式。
首先,我们来实现Teacher这个类:

package com.mec.about_java_observer;
import java.util.Observable;
//我们可以看出,Teacher这个类继承了Java中的Observable这个类
public class Teacher extends Observable{
    private String homeworkInfo; //定义一个字符串类型的作业变量

    public Teacher() {
    }
    //设置这个作业变量
    public void setHomework(String homeworkInfo) {
        this.homeworkInfo = homeworkInfo;
        System.out.println("老师今天布置的作业是:" + homeworkInfo);
        this.setChanged();  //当老师布置的作业信息有变化时调用这个方法
        this.notifyObservers();  //这个方法用于当老师布置的作业信息改变后,通知观察者
    }
    //获取这个作业信息
    public String getHomeworkInfo() {
        return homeworkInfo;
    }
}

接下来,我们实现观察者也就是学生这个类:

package com.mec.about_java_observer;
import java.util.Observable;
import java.util.Observer;

public class Student implements Observer{
    @SuppressWarnings("unused")
    private Teacher teacher;
    private String name;
    //这个构造方法中含有参数,其参数的含义是在初始化Student这个类的实例时会默认将name所对应的学生加这个老师所管理的列表中
    public Student(String name,Teacher teacher) {
        this.name = name;
        this.teacher = teacher;
        //将学生加入这个老师管理的学生列表中
        teacher.addObserver(this);
    }
    //实现Observer这个接口的方法,这个方法中有两个参数,我们在这个例子中使用的是第一个参数,第二个参数可以不用管。还有一点要注意,在使用这个参数时要将其类型强转
    @Override
    public void update(Observable arg0, Object arg1) {
        //类型强转,这也告诉我们,一个抽象类可以由它的继承类来实例化这个抽象类的对象,这里的抽象类是Observable ,它的继承类是Teacher 
        Teacher teacher = (Teacher) arg0;
        System.out.println(name + ":我收到的作业是:" + teacher.getHomeworkInfo());
    }
}

接下来,我们将两个类进行测试:

package com.mec.about_java_observer.test;
import com.mec.about_java_observer.Student;
import com.mec.about_java_observer.Teacher;

public class Test {
    public static void main(String[] args) {
        //定义一个Teacher类型的实例
        Teacher teacher = new Teacher();
        //定义三个Student的实例并初始化
        @SuppressWarnings("unused")
        Student studentOne = new Student("青菜", teacher);
        @SuppressWarnings("unused")
        Student studentTwo = new Student("白菜", teacher);
        Student studentThree = new Student("包菜", teacher);
        //发布作业的信息
        teacher.setHomework("将今天学习的内容自己下去练一练!");
        teacher.deleteObserver(studentThree);
        System.out.println("删除了一个同学之后的输出!\n");
        teacher.setHomework("尽管很枯燥,但是还是要继续加油,继续练下去!");
    }
}

最后,我们将测试结果输出一下:

老师今天布置的作业是:将今天学习的内容自己下去练一练!
包菜:我收到的作业是:将今天学习的内容自己下去练一练!
白菜:我收到的作业是:将今天学习的内容自己下去练一练!
青菜:我收到的作业是:将今天学习的内容自己下去练一练!
删除了一个同学之后的输出!

老师今天布置的作业是:尽管很枯燥,但是还是要继续加油,继续练下去!
白菜:我收到的作业是:尽管很枯燥,但是还是要继续加油,继续练下去!
青菜:我收到的作业是:尽管很枯燥,但是还是要继续加油,继续练下去!

好了,今天终于写了一篇比较完整的博客,相对于前面的几篇自己也比较满意。谢谢你还能看到这里!有不足之处,欢迎大家多多指教。不早了,晚安!

                    *以悠悠之年,立一技之长!*

一直很明媚!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值