文章目录
18. 观察者模式(Observer Pattern)
18.1 需求的引入
天气预报项目需求,具体要求如下:
- 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
- 需要设计开放型 API,便于其他第三方也能接入气象站获取数据。
- 提供温度、气压和湿度的接口
- 测量数据更新时,要能实时的通知给第三方
18.2 基本介绍
基本介绍
- 观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,它定义了一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
- 这种类型的设计模式属于行为型模式。
- 被依赖的对象为 Subject(被观察对象),依赖的对象为 Observer(观察者),Subject通知 Observer变化。
- Subject:登记注册、移除和通知
- registerObserver 注册
- removeObserver 移除
- notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送, 看具体需求定
- Observer:接收输入
18.3 应用实例
import java.util.ArrayList;
import java.util.List;
/**
* 观察者模式
*
* @author houyu
* @createTime 2020/2/8 11:49
*/
public class Demo {
public static void main(String[] args) {
// 创建气象局数据源(被观察者对象)
WeatherData weatherData = new WeatherData();
// 创建具体接入方(观察者对象)
BaiduSite baiduSite = new BaiduSite();
// 接入气象局数据
weatherData.registerObserver(baiduSite);
// 数据更新
weatherData.setData(10f, 100f, 30.3f);
weatherData.setData(10f, 102f, 30.3f);
}
/**
* 观察者接口
*/
public interface Observer {
/**
* @param temperature 温度
* @param pressure 气压
* @param humidity 湿度
*/
void update(float temperature, float pressure, float humidity);
}
public static class BaiduSite implements Observer {
float temperature, pressure, humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
this.display();
}
/**
* 显示
*/
public void display() {
System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***");
System.out.println("***百度网站 湿度: " + humidity + "***");
}
}
/**
* 被观察对象
*/
public interface Subject {
/**
* 添加观察者
*/
void registerObserver(Observer o);
/**
* 移除观察者
*/
void removeObserver(Observer o);
/**
* 通知观察者
*/
void notifyObservers();
}
/**
* 具体的被观察者对象
*/
public static class WeatherData implements Subject {
float temperature, pressure, humidity;
List<Observer> observerList = new ArrayList<>(8);
@Override
public void registerObserver(Observer o) {
observerList.add(o);
}
@Override
public void removeObserver(Observer o) {
observerList.remove(o);
}
@Override
public void notifyObservers() {
observerList.forEach(v -> v.update(temperature, pressure, humidity));
}
/**
* 当数据有更新时
* @param temperature
* @param pressure
* @param humidity
*/
public void setData(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
this.notifyObservers();
}
}
}
18.4 观察者模式在 JDK 的源码分析
- Observable 是一个类,充当了 Subject 的角色(被观察者以及具体被观察者)
public class Observable {
// ...
private Vector<Observer> obs;
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);
}
// ...
}
- Observer 的作用和地位等价于我们前面讲过的 Observer, 有 update
public interface Observer {
void update(Observable o, Object arg);
}
18.5 观察者模式的注意事项和细节
好处
- 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
- 我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码, 遵守了 ocp 原则。
19. 中介者模式(Mediator Pattern)
19.1 需求的引入
智能家庭项目:
- 智能家庭包括各种设备,闹钟、咖啡机、电视机、窗帘 等
- 主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹铃响起->咖啡机开始 做咖啡->窗帘自动落下->电视机开始播放
19.2 基本介绍
基本介绍
- 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地 相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- 中介者模式属于行为型模式,使代码易于维护
- 比如 MVC 模式,C(Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起 到了中间人的作用
原理类图
对原理类图的说明-即(中介者模式的角色及职责)
- Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
- Colleague 是抽象同事类(发送消息进行协调处理的基类)
- ConcreteMediator 具体的中介者对象, 实现抽象方法, 他需要知道所有的具体的同事类,即以一个集合来管理 HashMap,并接受某个同事对象消息,完成相应的任务
- ConcreteColleague 具体的同事类,会有很多, 每个同事只知道自己的行为,而不了解其他同事类的行为(方法), 但是他们都依赖中介者对象
19.3 应用实例
- 思路分析和图解(类图)
- 代码实现
import java.util.HashMap;
/**
* @author houyu
* @createTime 2020/2/8 13:54
*/
public class Demo {
public static void main(String[] args) {
// 创建一个中介者对象
Mediator mediator = new ConcreteMediator();
// 创建 Alarm 并且加入到 ConcreteMediator 对象的 HashMap
Alarm alarm = new Alarm("alarm", mediator);
// 创建了 CoffeeMachine 对象,并且加入到 ConcreteMediator 对象的 HashMap
CoffeeMachine coffeeMachine = new CoffeeMachine("coffeeMachine", mediator);
// 创建 Curtains , 并且加入到 ConcreteMediator 对象的 HashMap
Curtains curtains = new Curtains("curtains", mediator);
// 创建 TV , 并且加入到 ConcreteMediator 对象的 HashMap
TV tV = new TV("TV", mediator);
// 让闹钟发出消息
alarm.sendMessage(0);
coffeeMachine.finishCoffee();
alarm.sendMessage(1);
}
/**
* 抽象同事类(发送消息进行协调处理的基类)
*/
public static abstract class Colleague {
public String name;
private Mediator mediator;
public Colleague(String name, Mediator mediator) {
this.name = name;
this.mediator = mediator;
}
public Mediator getMediator() {
return mediator;
}
/**
* 发送消息
*/
public abstract void sendMessage(int stateChange);
}
/**
* 闹钟
*/
public static class Alarm extends Colleague {
public Alarm(String name, Mediator mediator) {
super(name, mediator);
mediator.register(name, this);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().sendMessage(stateChange, this.name);
}
}
/**
* 电视
*/
public static class TV extends Colleague {
public TV(String name, Mediator mediator) {
super(name, mediator);
mediator.register(name, this);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().sendMessage(stateChange, this.name);
}
public void startTv() {
System.out.println("It's time to StartTv!");
}
public void stopTv() {
System.out.println("StopTv!");
}
}
/**
* 窗帘
*/
public static class Curtains extends Colleague {
public Curtains(String name, Mediator mediator) {
super(name, mediator);
mediator.register(name, this);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().sendMessage(stateChange, this.name);
}
public void upCurtains() {
System.out.println("I am holding Up Curtains!");
}
}
/**
* 咖啡机
*/
public static class CoffeeMachine extends Colleague {
public CoffeeMachine(String name, Mediator mediator) {
super(name, mediator);
mediator.register(name, this);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().sendMessage(stateChange, this.name);
}
public void startCoffee() {
System.out.println("It's time to start coffee!");
}
public void finishCoffee() {
System.out.println("After 5 minutes!");
System.out.println("Coffee is ok!");
sendMessage(0);
}
}
/**
* 中介者
*/
public static abstract class Mediator {
/**
* 将给中介者对象,加入到集合中
*/
public abstract void register(String colleagueName, Colleague colleague);
/**
* 接收消息, 具体的同事对象发出
*/
public abstract void sendMessage(int stateChange, String colleagueName);
}
/**
* 具体中介者
*/
public static class ConcreteMediator extends Mediator {
private HashMap<String, Colleague> colleagueMap = new HashMap<>();
private HashMap<String, String> interMap = new HashMap<>();
@Override
public void register(String colleagueName, Colleague colleague) {
colleagueMap.put(colleagueName, colleague);
if(colleague instanceof Alarm) {
interMap.put("Alarm", colleagueName);
} else if(colleague instanceof CoffeeMachine) {
interMap.put("CoffeeMachine", colleagueName);
} else if(colleague instanceof TV) {
interMap.put("TV", colleagueName);
} else if(colleague instanceof Curtains) {
interMap.put("Curtains", colleagueName);
}
}
@Override
public void sendMessage(int stateChange, String colleagueName) {
if(colleagueMap.get(colleagueName) instanceof Alarm) {
// 如果 TV 发现消息(闹钟响了)
if(stateChange == 0) {
((CoffeeMachine) (colleagueMap.get(interMap.get("CoffeeMachine")))).startCoffee();
((TV) (colleagueMap.get(interMap.get("TV")))).startTv();
} else if(stateChange == 1) {
((TV) (colleagueMap.get(interMap.get("TV")))).stopTv();
}
} else if(colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
// 如果 咖啡机 发现信息
if(stateChange == 0) {
((CoffeeMachine) (colleagueMap.get(interMap.get("CoffeeMachine")))).startCoffee();
} else if(stateChange == 1) {
((CoffeeMachine) (colleagueMap.get(interMap.get("CoffeeMachine")))).finishCoffee();
}
} else if(colleagueMap.get(colleagueName) instanceof TV) {
// 如果 TV 发现消息
} else if(colleagueMap.get(colleagueName) instanceof Curtains) {
// 如果是以窗帘发出的消息,这里处理...
}
}
}
}
19.4 中介者模式在 JDK-Timer 应用的源码分析
前言:需要知道,上面的代码标准的写法是中介者实例通过构造方法传递给具体的同事类,但是也有一些写法是,具体的同时类传递给中介者(通过方法具体传递),从而实现了中介者管理以及通知具体同事类进行协调工作,如果JDK-Timer,JDK-Executor 等…
- Timer是一个中介者,它持有新创建到TimerTask的引用,timer负责执行TimerTask的定时任务
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("nice to meet u");
}
}, new Date(),1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("nice to meet u too");
}
}, new Date(),1000);
- java.util.Timer#schedule 中间完善传递
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
- java.util.Timer#sched 中介者实际处理的方法
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
19.5 中介者模式的注意事项和细节
- 多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
- 减少类间依赖,降低了耦合,符合迪米特原则
- 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
- 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意