当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
场景
一个小baby在睡觉,如果小baby醒了会哭,所以有一个观察者在监听是否睡醒了,小baby在这里就是被观察者,代码如下
class Child{
private boolean cry =false;
public boolean isCry(){
return cry;
}
public void wakeUp(){
System.out.println("wake up!crying wuwuwuwuwu...");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
while (!child.isCry()){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("observing...");
}
}
}
主函数会一直等待,傻等
这时候我在child类中加入一个爸爸的观察者,当baby醒了后调用wakeUp方法,爸爸会喂他
/**
* @Author huyouting
* @Date 2021/2/22 10:00
* @Description:
*/
class Child{
private boolean cry =false;
public boolean isCry(){
return cry;
}
public void wakeUp(){
System.out.println("wake up!crying wuwuwuwuwu...");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
while (!child.isCry()){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("observing...");
}
}
}
如果有多个观察者,比如当baby醒了后,爸爸会喂他,妈妈会抱抱他,狗狗会叫,这时的代码如下:
class Child{
private boolean cry =false;
private Dad dad = new Dad();
private mum mum = new mum();
private Dog dog = new Dog();
public boolean isCry(){
return cry;
}
public void wakeUp(){
cry =true;
dad.feed();
mum.hug();
dog.wang();
}
}
class Dad{
public void feed(){
System.out.println("dad feeding...");
}
}
class mum{
public void hug(){
System.out.println("mum hugging...");
}
}
class Dog{
public void wang(){
System.out.println("Dog wang...");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
//do sth
child.wakeUp();
}
}
问题思考
一个被观察者有很多观察者,每个观察者的处理逻辑不同,比如小狗看到baby醒了会叫,如果看到了一只小鸟也会叫,观察者做出的反应不应该耦合到某一个具体的被观察者身上。如果我再加一个新的观察者,是不是又得在被观察者类中添加新的观察者,这样的耦合度是不是很高?如何解耦?
解决方法:
定义一个观察者接口,所有观察者都得实现这个接口
interface Observer{
void actionOnWakeUp();
}
actionOnWakeUp()就是小baby醒了后的处理方法,方法并不确定,由具体的实现类去实现。
具体的观察者代码
class Dad implements Observer{
public void feed(){
System.out.println("dad feeding...");
}
@Override
public void actionOnWakeUp() {
feed();
}
}
class Mum implements Observer{
public void fug(){
System.out.println("mum fugging...");
}
@Override
public void actionOnWakeUp() {
fug();
}
}
class Dog implements Observer{
public void wang(){
System.out.println("dog wanging...");
}
@Override
public void actionOnWakeUp() {
wang();
}
}
child类和主函数
class Child{
private boolean cry =false;
private List<Observer> observers = new ArrayList<>();
{
observers.add(new Dad());
observers.add(new Mum());
observers.add(new Dog());
}
public boolean isCry(){
return cry;
}
public void wakeUp(){
cry =true;
for (Observer observer : observers) {
observer.actionOnWakeUp();
}
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.wakeUp();
}
}
很多时候观察者需要根据被观察者的具体情况做出相应的处理,比如说baby哭的时间,地点不同,则观察者做出的反应也不同,所以我们得抽象出一个事件类Event,当baby醒了后得发出这个事件,观察者通过这个事件做出相应的处理。所以重新定义了Observer接口,当baby醒了后,产生了事件的时间为当前时间,地点在床上,并在actionOnWakeUp方法中传递了event(事件)参数,具体代码实现如下:
class Child {
private boolean cry = false;
private List<Observer> observers = new ArrayList<>();
{
observers.add(new Dad());
observers.add(new Mum());
observers.add(new Dog());
}
public boolean isCry() {
return cry;
}
public void wakeUp() {
cry = true;
wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(),"bed");
for (Observer observer : observers) {
observer.actionOnWakeUp(event);
}
}
}
//事件类 fireEvent
class wakeUpEvent {
long timestamp;
String loc;
public wakeUpEvent(long timestamp, String loc) {
this.timestamp = timestamp;
this.loc = loc;
}
}
interface Observer {
void actionOnWakeUp(wakeUpEvent event);
}
class Dad implements Observer {
public void feed() {
System.out.println("dad feeding...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
feed();
}
}
class Mum implements Observer {
public void fug() {
System.out.println("mum fugging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
fug();
}
}
class Dog implements Observer {
public void wang() {
System.out.println("dog wanging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
wang();
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.wakeUp();
}
}
大多数时候,我们处理事件的时候,需要事件源对象,因为观察者做出的反应可能是根据不同事件源做出反应的,比如小狗不只是当baby醒了后会叫,当看见一只小猫或者小鸡的时候也会叫,所以当处理具体的逻辑时需要知道是哪个事件源发出的,观察者的动作和事件源本身并不是完完全全耦合的,不同的事件源可能有同一个观察者,一个事件源也可能有多个观察者,事件源也可以发出好多个不同的事件。所以,事件源和事件和观察者的耦合度没那么高。具体代码实现如下:
/**
* @Author huyouting
* @Date 2021/2/22 10:17
* @Description:
*/
/**
* 有很多时候,观察者需要根据事件的具体情况来进行处理
* 大多数时候,我们处理事件的时候,需要事件源对象
*/
class Child {
private boolean cry = false;
private List<Observer> observers = new ArrayList<>();
{
observers.add(new Dad());
observers.add(new Mum());
observers.add(new Dog());
}
public boolean isCry() {
return cry;
}
public void wakeUp() {
cry = true;
wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed",this);
for (Observer observer : observers) {
observer.actionOnWakeUp(event);
}
}
}
//事件类 fireEvent
class wakeUpEvent {
long timestamp;
String loc;
Child source;
public wakeUpEvent(long timestamp, String loc,Child source) {
this.timestamp = timestamp;
this.loc = loc;
this.source=source;
}
}
interface Observer {
void actionOnWakeUp(wakeUpEvent event);
}
class Dad implements Observer {
public void feed() {
System.out.println("dad feeding...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
feed();
}
}
class Mum implements Observer {
public void fug() {
System.out.println("mum fugging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
fug();
}
}
class Dog implements Observer {
public void wang() {
System.out.println("dog wanging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
wang();
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.wakeUp();
}
}
事件也可以形成继承体系,定义一个Event事件的父类,所有Event子类继承
class Child {
private boolean cry = false;
private List<Observer> observers = new ArrayList<>();
{
observers.add(new Dad());
observers.add(new Mum());
observers.add(new Dog());
observers.add((e)->{
System.out.println("ppp");
});
//hook callback
}
public boolean isCry() {
return cry;
}
public void wakeUp() {
cry = true;
wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed",this);
for (Observer observer : observers) {
observer.actionOnWakeUp(event);
}
}
}
abstract class Event<T>{
abstract T getSource();
}
//事件类 fireEvent
class wakeUpEvent extends Event<Child> {
long timestamp;
String loc;
Child source;
public wakeUpEvent(long timestamp, String loc,Child source) {
this.timestamp = timestamp;
this.loc = loc;
this.source=source;
}
@Override
Child getSource() {
return source;
}
}
interface Observer {
void actionOnWakeUp(wakeUpEvent event);
}
class Dad implements Observer {
public void feed() {
System.out.println("dad feeding...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
feed();
}
}
class Mum implements Observer {
public void fug() {
System.out.println("mum fugging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
fug();
}
}
class Dog implements Observer {
public void wang() {
System.out.println("dog wanging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
wang();
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.wakeUp();
}
}