观察者模式主要存在于一对多的情况下,一个对象的修改需要通知多个依赖它的对象,它属于行为型的一种模式;
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
这里举一个例子:一家三口外加一只大黄狗,当一个孩子哭了或撒尿了,作为观察者的大黄狗 爸爸 妈妈 会做出一系列的反应;
下图是观察者模式的简单类图:
实现观察者模式主要分以下几个步骤:
- 创建被观察者接口
- 具体的观察者实现接口
- 创建事件抽象接口
- 具体的事件继承抽象类
- 创建被观察者,添加观察者和事件
1、创建被观察者接口
package com.dongl.observer; /** * 定义一个观察者接口 */ public interface Observer { //对孩子醒做出反应 void actionOnWakeUp(WakeUpEvent event); //对孩子撒尿做出反应 void actionOnPee(PeeEvent event);}
2、具体的观察者实现接口
爸爸:
package com.dongl.observer; import java.text.SimpleDateFormat; /** * 爸爸是一个观察者 */ public class Dad implements Observer { @Override public void actionOnWakeUp(WakeUpEvent event) { String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(event.getTimestamp()); feed(event.clazz.getClass().getSimpleName(),date ,event.getLoc(), event.toDo ); } @Override public void actionOnPee(PeeEvent event) { String date = new SimpleDateFormat("yyyy-MM-dd HH:mm;ss").format(event.getTimestamp()); System.out.println( event.clazz.getClass().getSimpleName() + date +event.getLoc() + event.toDo +"爸爸 不闻不问------------"); } //爸爸醒后可能会做很多事情 public void feed(String simpleName, String date, String loc, String todo) { System.out.println(simpleName+ date+ loc+ todo+"爸爸 给孩子喂食 ---------------"); } }
妈妈:
package com.dongl.observer; public class Mum implements Observer { @Override public void actionOnWakeUp(WakeUpEvent event) { hug(); } @Override public void actionOnPee(PeeEvent event) { diaper(); } private void diaper() { System.out.println("妈妈给孩子换尿布--------------"); } private void hug() { System.out.println("妈妈抱起孩子 -------------------"); } }
大黄狗:
package com.dongl.observer; /** * 狗也是孩子的观察者 */ public class Dog implements Observer{ @Override public void actionOnWakeUp(WakeUpEvent event) { wang(); } @Override public void actionOnPee(PeeEvent event) { System.out.println("老黄狗 不闻不问--------------"); } //狗叫 private void wang() { System.out.println("老黄狗 wang wang wang 叫个不停-----------"); } }
3、创建事件抽象接口
package com.dongl.observer; /** * 定义一个事件的抽象类 * @param <T> */ public abstract class Event<T> { //谁 T clazz; //时间 long timestamp; //位置 String loc; //干了什么 String toDo; public T getClazz() { return clazz; } public void setClazz(T clazz) { this.clazz = clazz; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public String getLoc() { return loc; } public void setLoc(String loc) { this.loc = loc; } public String getToDo() { return toDo; } public void setToDo(String toDo) { this.toDo = toDo; } abstract T getSource(); }
4、具体的事件继承抽象类
孩子醒了:
package com.dongl.observer; /** * 具体的时间类 孩子醒事件 */ public class WakeUpEvent extends Event<Child> { public WakeUpEvent(long timestamp, String loc,String todo ,Child child) { this.timestamp = timestamp; this.loc = loc; this.toDo = todo; this.clazz = child; } @Override Child getSource() { return this.clazz; } }
孩子撒尿:
package com.dongl.observer; public class PeeEvent extends Event<Child>{ public PeeEvent(long timestamp, String loc,String todo , Child child) { this.timestamp = timestamp; this.loc = loc; this.toDo = todo; this.clazz = child; } @Override Child getSource() { return this.clazz; } }
5、创建被观察者,添加观察者和事件
package com.dongl.observer; import java.util.ArrayList; import java.util.List; public 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 void wakeUp(long time ,String loc ,String todo){ /** * 把孩子醒作为一个事件 */ WakeUpEvent event = new WakeUpEvent(time, loc, todo,this); //分别通知所有的观察者 让他们做出相应的反应和动作 observers.forEach(item -> item.actionOnWakeUp(event)); } //孩子撒尿 public void pee(long time ,String loc, String todo){ PeeEvent peeEvent = new PeeEvent(time , loc ,todo, this); observers.forEach(item -> item.actionOnPee(peeEvent)); } }
下面是一个测试类:
package com.dongl.observer; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args) { Child child = new Child(); System.out.println("-------------------孩子要哭了哟 ---------------------"); child.wakeUp(System.currentTimeMillis() , "bed" ,"哭"); //睡两秒 try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("----------------------孩子要撒尿了哟 ---------------------"); child.pee(System.currentTimeMillis() , "bed","撒尿"); } }