观察者模式在实际开发应用中很常见。很多的源码用的也很多,例如spring中常见的事件机制就是观察者模式,观察者模式也可以看作发布/订阅模式
从实际生活中可以举一些例子:
1:交通信号灯(目标,被观察者)
2:人(观察者,分步行人,骑自行车人,开车人)
人观察信号灯这个目标,如果信号灯发生改变,则人开始根据信号灯的颜色做出反应
分析理解:信号灯改变是一个事件,这个事件该怎么通知出去到行人?人接收到事件改变后根据自己的交通工具做出反应。
解决:信号灯要能通知到每个人,那么这个信号灯必须知道有哪些人在看它。类比,信号灯这个抽象 Light(观察者观察的目标) 必须有一个列表属性 Vector<人> (观察者)(线程同步的列表)
如果Light.changed(),遍历这个Vector,调用<人>的doSomeThing(),从而这个人可以对事件改变做出反应
这种设计模式很简单,你可以自己写一个,或者利用jdk提供的Observer(观察者)和Observable(可观察的事物,可以理解成目标的意思)
- Observer源码
package java.util; public interface Observer { void update(Observable o, Object arg); }
这个就是观察者了,观察者接口中只有一个update方法,参数是一个Observable(目标)和一个arg参数,这个参数类型不限定
这个update什么时候调用呢?我们在上面已经讲过了 “如果Light.changed(),遍历这个Vector,调用<人>的doSomeThing(),从而这个人可以对事件改变做出反应”
这个update就是人的doSomeThing(),这个方法是给目标准备的,当目标改变的时候通过update这个方法来让观察者做出反应,既然如此jdk有没有提供目标或者被观察
的事物呢?
- Observable
package java.util; 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(); } }
先看
通知观察者notifyObservers() 这个方法,
synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg);
方法中有个changed变量,这个变量默认false,该方法将直接返回,所以方法要向下执行到 观察者的update()方法,则setChange()这个方法必须在
notifyObservers()调用之前被调用 ,现在我们来实现这个信号灯作为目标,人作为观察者的观察者模式,
目标信号灯添加观察者人(可以叫注册,也可以叫监听),目标信号灯发生改变时,人接收到信号灯改变时,做出相应的行为动作:
- 目标信号灯对象
import java.util.Observable; public class LightObservable extends Observable { public void greenLight() { setChanged(); notifyObservers("绿灯了..."); } }
目标信号灯发出路灯信号,该事件发生,但是此时还没有任何人关注到该信号灯的改变(因为上文提到:目标信号灯添加观察者人(可以叫注册,也可以叫监听))
所以我们先要添加关注信号灯的人:
import java.util.Observable; import java.util.Observer; public class PasserbyObserver implements Observer { @Override public void update(Observable o, Object arg) { System.out.println((String)arg+"我可以过马路了..."); } }
import java.util.Observable; import java.util.Observer; public class RideManObserver implements Observer { @Override public void update(Observable o, Object arg) { System.out.println((String)arg+"我可以骑车过马路了!"); } }
import java.util.Observable; import java.util.Observer; public class DriveManObserver implements Observer { @Override public void update(Observable o, Object arg) { System.out.println((String)arg+"我可以开车过去了"); } }
现在来添加不同的人到目标中(可以叫注册或者监听)
import java.util.Observer; public class Test { public static void main(String[] args) { //目标 LightObservable ob = new LightObservable(); //步行观察者 Observer passerbyOb = new PasserbyObserver(); //骑车人观察者 Observer rideManOb = new RideManObserver(); //骑车人观察者 Observer driverManOb = new DriveManObserver(); //注册不同观察者 ob.addObserver(passerbyOb); ob.addObserver(rideManOb); ob.addObserver(driverManOb); ob.greenLight(); } }
说明:当目标信号灯中已经存在观察者的时候,现在可以发送路灯事件了
执行后结果:
绿灯了...我可以开车过去了
绿灯了...我可以骑车过马路了!
绿灯了...我可以过马路了...
自己实现的不同部门给不同员工发送信息,直接贴代码:
/** * 公司部门接口 * @author sky * */ public interface Dept { /**公司部门添加指定的员工*/ public void addEmployee(Employee employeeInterface); /**公司部门删除指定的员工*/ public void remEmployee(Employee employeeInterface); /**公司部门发布消息*/ public void sendMeetingMessage(String Message); }
//实现了公司部门的抽象类 public abstract class AbstractDept implements Dept { //定义一个员工接口集合 List<Employee> employeeList = new ArrayList<>(); @Override public void addEmployee(Employee employeeInterface) { this.employeeList.add(employeeInterface); } @Override public void remEmployee(Employee employeeInterface) { if(!CollectionUtils.isEmpty(employeeList)) { this.employeeList.remove(employeeInterface); } } }
/** * 信息部门 * @author sky * */ public class InformationDept extends AbstractDept { @Override public void sendMeetingMessage(String message) { for (Employee employeeInterface : employeeList) { employeeInterface.recivedMessage(message); } } }
/** * 财务部门 * @author sky * */ public class FinanceDept extends AbstractDept { @Override public void sendMeetingMessage(String message) { for (Employee employeeInterface : employeeList) { employeeInterface.recivedSalayMessage(message); } } }
//员工接口 public interface Employee { //员工接收消息 public void recivedMessage(String message); //员工接收工资消息 public void recivedSalayMessage(String message); }
/** * 全体员工 * @author sky * */ public class CommonEmployee implements Employee { public CommonEmployee(String name) { super(); this.name = name; } public CommonEmployee(String name, double salary) { super(); this.name = name; this.salary = salary; } //员工名称 private String name; //员工工资 private double salary; public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void recivedMessage(String message) { System.out.println("通知:"+this.name+message); } @Override public void recivedSalayMessage(String message) { System.out.println("通知:"+this.name+":"+this.salary+message); } }
/** * 小组领导 * @author sky * */ public class GroupLeaderEmployee implements Employee { //小组领导名称 private String name; //员工工资 private double salary; public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public GroupLeaderEmployee(String name) { super(); this.name = name; } public GroupLeaderEmployee(String name, double salary) { super(); this.name = name; this.salary = salary; } @Override public void recivedMessage(String message) { System.out.println("通知:"+this.name + message); } @Override public void recivedSalayMessage(String message) { System.out.println("通知:"+this.name+":"+this.salary + message); } }
测试:
public class TestSendMessage { public static void main(String[] args) { //========================信息部门发布开会消息=========================== //信息部门 InformationDept informationDept = new InformationDept(); //员工组长A、B、C GroupLeaderEmployee groupLeaderA = new GroupLeaderEmployee("领导A",3100); GroupLeaderEmployee groupLeaderB = new GroupLeaderEmployee("领导B",3099); GroupLeaderEmployee groupLeaderC = new GroupLeaderEmployee("领导C",3098); //信息部门给组长发送消息 informationDept.addEmployee(groupLeaderA); informationDept.addEmployee(groupLeaderB); informationDept.addEmployee(groupLeaderC); informationDept.sendMeetingMessage("今天下午3点会议室201组长开会..."); //信息部门给所有人发送消息 CommonEmployee employeeA = new CommonEmployee("员工A",2100); CommonEmployee employeeB = new CommonEmployee("员工B",2200); CommonEmployee employeeC = new CommonEmployee("员工C",2300); informationDept.addEmployee(employeeA); informationDept.addEmployee(employeeB); informationDept.addEmployee(employeeC); informationDept.sendMeetingMessage("今天下午5点会议室201全体开会..."); //=======================财务部门发布所有员工的工资发放通知================ //财务部门 FinanceDept financeDept = new FinanceDept(); //财务部门给所有人发送工资发放通知 financeDept.addEmployee(employeeA); financeDept.addEmployee(employeeB); financeDept.addEmployee(employeeC); financeDept.addEmployee(groupLeaderA); financeDept.addEmployee(groupLeaderB); financeDept.addEmployee(groupLeaderC); financeDept.sendMeetingMessage("工资已发放,请注意查收!"); } }
返回结果:
通知:领导A今天下午3点会议室201组长开会... 通知:领导B今天下午3点会议室201组长开会... 通知:领导C今天下午3点会议室201组长开会... 通知:领导A今天下午5点会议室201全体开会... 通知:领导B今天下午5点会议室201全体开会... 通知:领导C今天下午5点会议室201全体开会... 通知:员工A今天下午5点会议室201全体开会... 通知:员工B今天下午5点会议室201全体开会... 通知:员工C今天下午5点会议室201全体开会... 通知:员工A:2100.0工资已发放,请注意查收! 通知:员工B:2200.0工资已发放,请注意查收! 通知:员工C:2300.0工资已发放,请注意查收! 通知:领导A:3100.0工资已发放,请注意查收! 通知:领导B:3099.0工资已发放,请注意查收! 通知:领导C:3098.0工资已发放,请注意查收!