Java设计模式:观察者模式

观察者模式简介

观察者模式(Observer Pattern)是一种行为设计模式目的是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都能得到通知并自动更新。其核心思想是将被观察的对象(主题,Subject)和观察它的对象(观察者,Observer)分离,使得它们可以独立地改变和复用 。主题维护一个观察者列表,当主题状态改变时,通过调用观察者的更新方法来通知它们,实现了低耦合的事件通知机制。

通过一个简单的问题来描述观察者模式中所涉及的各个角色。一个大学生和一个归国留学生都希望及时知道“求职中心”最新的职位需求信息。

类图

在这里插入图片描述

  1. 主题
    主题是 Subject 接口,该接口规定了具体主题需要实现的添加观察者、删除观察者以及发送数据给观察者的方法。代码如下:
public interface Subject{
    public void addObserver(Observer o);
    public void deleteObserver(Observer o);
    public void notifyObservers(String str);
}
  1. 观察者
    观察者是 Observer 接口。该接口规定了具体观察者用来更新数据的方法。对于本问题,观察者接口规定的方法是 hearTelephone(相当于观察者模式类图中的 update 方法),即要求具体观察者通过实现 hearTelephone 方法(模拟接听电话)来更新数据。Observer 接口的代码如下:
public interface Observer{
    public void hearTelephone(String heardMess);
}
  1. 具体主题
    具体主题是 SeekJobCenter 类。具体主题通过实现 notifyObservers 方法来通知具体观察者,实现的方式是遍历具体主题中用来存放观察者的集合,并让集合中的每个具体观察者执行观察者接口(Observer)规定更新数据的方法,例如 hearTelephone 方法。SeekJobCenter 类的代码如下:
import java.util.ArrayList;
public class SeekJobCenter implements Subject{
    ArrayList<Observer> personList; //存放观察者对象的数组表
    public SeekJobCenter(){
        personList = new ArrayList<Observer>();
    }
    public void addObserver(Observer observer){
        if(!(personList.contains(observer)))
            personList.add(observer); //把观察者对象添加到数组表
    }
    public void deleteObserver(Observer observer){
        if(personList.contains(observer))
            personList.remove(observer);
    }
    public void notifyObservers(String mess){ //通知所有的观察者
        for(int i = 0; i < personList.size(); i++){
            Observer observer = personList.get(i);
            observer.hearTelephone(mess); //让观察者接听电话
        }
    }
}
  1. 具体观察者
    具体观察者是 Student 类和 HaiGui 类。Student 类的实例调用 hearTelephone(String heardMess) 方法时,会将收到的数据保存到一个文件中。HaiGui 类的实例调用 hearTelephone(String heardMess) 方法时,如果数据中包含“程序员”或“软件”等字样,就将信息保存到一个文件中。
    Student 类代码:
import java.io.*;
public class Student implements Observer{
    Subject subject;
    File myFile;
    public Student(Subject subject, String fileName){
        this.subject = subject;
        subject.addObserver(this); //使当前对象成为 subject 具体主题的观察者
        myFile = new File(fileName);
    }
    public void hearTelephone(String heardMess){
        try{
            RandomAccessFile out = new RandomAccessFile(myFile, "rw");
            out.seek(out.length());
            byte b[] = heardMess.getBytes();
            out.write(b); //更新文件中的内容
            System.out.print("我是一个大学生,");
            System.out.println("我向文件" + myFile.getName() + "写入如下内容:");
            System.out.println(heardMess);
        }catch(IOException exp){
            System.out.println(exp.toString());
        }
    }
}

HaiGui 类代码:

import java.io.*;
public class HaiGui implements Observer{
    Subject subject;
    File myFile;
    public HaiGui(Subject subject, String fileName){
        this.subject = subject;
        subject.addObserver(this); //使当前对象成为 subject 具体主题的观察者
        myFile = new File(fileName);
    }
    public void hearTelephone(String heardMess){
        try{
            boolean isOK = heardMess.contains("Java程序员") || heardMess.contains("软件");
            RandomAccessFile out = new RandomAccessFile(myFile, "rw");
            if(isOK){
                out.seek(out.length());
                byte b[] = heardMess.getBytes();
                out.write(b); //更新文件中的内容
                System.out.print("我是一个海归,");
                System.out.println("我向文件" + myFile.getName() + "写入如下内容:");
                System.out.println(heardMess);
            }
        }catch(IOException exp){
            System.out.println(exp.toString());
        }
    }
}

应用程序 Application.java 使用了上述类,演示一个大学生和一个归国留学生成为求职中心的观察者;当求职中心有新的人才需求信息时,大学生和归国留学生将得到通知。

public class Application{
    public static void main(String args[]){
        Subject center = new SeekJobCenter(); //具体主题
        Observer zhang = new Student(center, "A.txt"); //具体观察者
        Observer wang = new HaiGui(center, "B.txt"); //具体观察者
        center.notifyObservers("星星公司需要20个Java程序员."); //主题通知信息
        center.notifyObservers("月月公司需要8个动画设计师.");
        center.notifyObservers("星月公司需要9个电工.");
    }
}

运行结果

我是一个大学生,我向文件A.txt写入如下内容:  
星星公司需要20个Java程序员。  
我是一个海归,我向文件B.txt写入如下内容:  
星星公司需要20个Java程序员。  
我是一个大学生,我向文件A.txt写入如下内容:  
月月公司需要8个动画设计师。  
我是一个大学生,我向文件A.txt写入如下内容:  
星月公司需要9个电工。 

上述代码对应的类图puml代码

@startuml
interface Subject {
  +addObserver(Observer o)
  +deleteObserver(Observer o)
  +notifyObservers(String str)
}

interface Observer {
  +hearTelephone(String heardMess)
}

class SeekJobCenter {
  -ArrayList<Observer> personList
  +SeekJobCenter()
  +addObserver(Observer observer)
  +deleteObserver(Observer observer)
  +notifyObservers(String mess)
}

class Student {
  -Subject subject
  -File myFile
  +Student(Subject subject, String fileName)
  +hearTelephone(String heardMess)
}

class HaiGui {
  -Subject subject
  -File myFile
  +HaiGui(Subject subject, String fileName)
  +hearTelephone(String heardMess)
}

class Application {
  +main(String args[])
}

Subject <|.. SeekJobCenter
Observer <|.. Student
Observer <|.. HaiGui
SeekJobCenter --> Observer : personList
Student --> Subject : subject
HaiGui --> Subject : subject
@enduml

上述puml代码中:

  • 定义了 Subject 接口,包含添加观察者、删除观察者和通知观察者的方法。
  • Observer 接口定义了接收通知更新数据的 hearTelephone 方法。
  • SeekJobCenter 类实现了 Subject 接口,维护观察者列表并实现相关通知方法。
  • StudentHaiGui 类实现了 Observer 接口,各自有处理通知数据的逻辑。
  • Application 类是程序入口,用于测试和演示整个观察者模式的运行。类之间的关系通过箭头表示,体现了实现、关联等关系。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐