概念
观察者模式
发布/订阅模式(比生产消费更抽象)参考
基于典型观察者模式实现的生产者和消费者弊端:观察者模式中观察者需要直接订阅目标事件(直接引用目标状态),改变后直接接收响应,由于函数调用是同步的(阻塞),在消费者的方法没有返回之前,生产者只好一直等在那边,阻塞进程。 生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据,并不用关心消费者状态; 发布订阅模式就类似的多了一个发布通道(调度中心),一方面从生产者接收事件一方面向订阅者发布事件。1. 观察者模式的模板
设计阶段//目标对象,它持有观察者抽象的集合
public class Subject {
//定义抽象目标的状态
private String subjectState;
//用来保存注册的观察者集合
private List<Observer_> observerList = new ArrayList<Observer_>();
//当状态改变时去通知
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
this.notifyObservers(); //通知
}
//添加观察者
public void attach(Observer_ observer){
observerList.add(observer);
}
//删除观察者
public void detach(Observer_ observer){
observerList.remove(observer);
}
//通知所有的观察者
public void notifyObservers(){
for (Observer_ observer: observerList){
observer.updata(this);
}
}
}
//观察者
public class Observer_ {
//定义抽象的状态
private String observerState;
//获取目标类的状态同步到观察者状态中
public void updata(Subject subject){
observerState = subject.getSubjectState();
System.out.println("通知:" + observerState);
}
}
public class Client {
@Test
public void testObserver(){
//1 创建目标
Subject subject = new Subject();
//2 创建观察者
Observer_ observer1 = new Observer_();
observer1.setObserverState("observer1");
//注册
subject.attach(observer1);
//通知
subject.setSubjectState("这是观察者模式");
}
}
以上就是典型的观察者模式的模板
2. 区别对待观察者场景的问题
需求:最近经常加班,身为项目经理的你决定给大家买宵夜,张三只要鸡翅,李四想要鸡翅或者汉堡。这里宵夜种类就是目标,张三李四就是观察者,如果通过以上代码,是所有注册都会被通知,这显然是不合理的。代码修改如下:
观察者的接口
//观察者接口定义一个更新接口的方法,目标发生改变时通知观察者进行调用
public interface Observer {
//更新的接口
void update(SupperObject objecr);
//设置和取得观察者名称
void setObserverName(String observerName);
String getObserverName();
}
抽象的目标类
public abstract class SupperObject {
//持有的观察者集合
public List<Observer> observers = new ArrayList<Observer>();
//添加 删除 观察者
public void attach(Observer observer){
observers.add(observer);
}
public void detach(Observer observer){
observers.remove(observer);
}
//具体的提醒在子类逻辑中完成
protected abstract void notifyObservers();
}
//具体的目标实现类
public class ConcreteSupperSubject extends SupperObject {
//定义宵夜的变量,鸡翅,汉堡, 就是根据这个变量进项更新(目标状态)
private String supperContent;
@Override
protected void notifyObservers() {
//循环所有注册的观察者
for (Observer observer:observers){
//定义业务逻辑
//1 .如果鸡翅就通知张三和李四,2.如果汉堡就只通知李四,3.其他都不通知
if ("鸡翅".equals(this.getSupperContent())){
if ("张三".equals(observer.getObserverName())){
observer.update(this);
}
if ("李四".equals(observer.getObserverName())){
observer.update(this);
}
}
if ("汉堡".equals(this.supperContent)){
if ("李四".equals(observer.getObserverName())){
observer.update(this);
}
}
}
}
public String getSupperContent() {
return supperContent;
}
public void setSupperContent(String supperContent) {
this.supperContent = supperContent;
this.notifyObservers(); //通知 区别对待观察者
}
}
//观察者的具体实现
public class ConcreteObserver implements Observer{
//定义观察者名称 ,宵夜情况,提醒内容
private String observerName;
private String supperContent; //目标处获取的状态
private String remindThing;
@Override
public void update(SupperObject object) {
//获取目标的状态
supperContent = ((ConcreteSupperSubject)object).getSupperContent();
System.out.println(observerName+"收到了"+supperContent+remindThing);
}
}
@Test
public void testSupperClient(){
//创建目标
ConcreteSupperSubject supperSubject = new ConcreteSupperSubject();
//创建观察者
ConcreteObserver zhangsan = new ConcreteObserver();
zhangsan.setObserverName("张三");
zhangsan.setRemindThing("鸡翅真好吃!");
ConcreteObserver lisi = new ConcreteObserver();
lisi.setObserverName("李四");
lisi.setRemindThing("夜宵不错,吃饱了可以好好干活了");
//注册
supperSubject.attach(zhangsan);
supperSubject.attach(lisi);
//发布 鸡翅,汉堡
supperSubject.setSupperContent("汉堡");
}
观察者模式总结
- 存在目标对象和观察者两个概念,做到解耦,但通知却依赖了抽象的观察者,假如观察者无法抽象就无法通知更新。
- 所有的观察者的动作都一样。如果不一样就不能实现
事件机制
事件机制 就可以解决以上的问题,不需要观察者的抽象。通过相应的listener代替观察者,类似观察者模式却解耦目标和观察。一般把事件对象作为业务接口的参数,再根据相应的条件触发
事件机制一般需要3个角色,事件触发源(source)、事件状态对象(event)、处理逻辑(listener)
在spirng 中ServletContextListener接口通过web中的listener就可以在web启动时初始化spirng也是事件的一种应用。
- 定义事件类
//定义的事件状态类 也可以继承java util 中的event
public class MyEvent {
//事件对象
private Object obj;
//状态即触发条件
private String state;
/**
* Constructs a prototypical MyEvent.
*
* @param source The object on which the MyEvent initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public MyEvent(Object source, String state) {
//super(source);
this.obj = source;
this.state = state;
}
}
- 定义事件源handler
public class EventSource {
//保存监听器的列表,类似观察者模式中保存所有观察者的集合;子类可以保存自己的监听器。
private Collection listenets;
//注册监听器
public void addEventListener(EventListener e){
if (listenets == null){
listenets = new HashSet();
}
listenets.add(e);
}
//删除监听
public void removeListener(EventListener e){
if (listenets != null){
listenets.remove(e);
}
}
/** 根据触发的条件进行事件的执行
* @param state 触发条件
*/
protected void fireEvent(String state){
if (listenets != null){
MyEvent event = new MyEvent(this,state);
notifyA(event);
}
}
/**
* 事件具体的执行
* @param e 定义的事件
*/
public void notifyA(MyEvent e){
Iterator iterator = listenets.iterator();
while (iterator.hasNext()){
//实例监听器对象,并调用监听器的方法
EventListener evt = (EventListener) iterator.next();
evt.handEvent(e);
}
}
}
- 定义监听器,把事件作为参数,也是和观察者模式的区别
public interface EventListener{
//把事件对象作为参数
void handEvent(MyEvent e);
}
- 实现listener 接口,定义业务逻辑
public class Mylistener implements EventListener {
@Override
public void handEvent(MyEvent e) {
//todo 使用if做逻辑控制
if (e.getState() != null && e.getState() == "hi" ){
System.out.println(" hi ,java事件机制");
}
}
}
public class Mylistener2 implements EventListener {
@Override
public void handEvent(MyEvent e) {
if (e.getState() != null && e.getState().equals("h")){
System.out.println(" 我是被 "+e.getState()+" 触发的");
}
}
}
- 测试
public static void main(String[] args){
//事件源
EventSource source = new EventSource();
/*
* 当有需要的事件要被触发时就需要编写并注册 相关的listener
* 这也是事件的弊端
* */
source.addEventListener(new Mylistener());
source.addEventListener(new Mylistener2());
//根据状态触发
source.fireEvent("hi");
source.fireEvent("h");
}