设计模式之16观察者模式(笔记)

1 定义:Observer Pattern,也叫做发布订阅模式(Publish / subscribe

1.1 定义:Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。)

1.2 通用类图:

设计模式之16观察者模式(笔记)_第1张图片

1.3 通用代码:

public abstract class Subject {
	// 定一个一个观察者数组
	private Vector<Observer> obsVector = new Vector<Observer>();

	// 增加一个观察者
	public void addObserver(Observer o) {
		this.obsVector.add(o);
	}

	// 删除一个观察者
	public void delObserver(Observer o) {
		this.obsVector.remove(o);
	}

	// 通知所有观察者
	public void notifyObserver() {
		for (Observer o : this.obsVector) {
			o.update();
		}
	}
}

public interface Observer {
	// 更新方法
	public void update();
}

public class ConcreteSubject extends Subject {
	// 具体的业务
	public void doSomething() {
		/*
		 * do something
		 */
		super.notifyObserver();
	}
}

public class ConcreteObserver implements Observer {
	// 实现更新方法
	public void update() {
		System.out.println("接收到信息,并进行处理!");
	}
}

public class Test {
	public static void main(String[] args) {
		// 创建一个被观察者
		ConcreteSubject subject = new ConcreteSubject();
		// 定义一个观察则
		Observer obs = new ConcreteObserver();
		// 观察者观察被被观察则
		subject.addObserver(obs);
		// 观察者开始活动了
		subject.doSomething();
	}
}


2 优点

2.1 观察者与被观察者之间是抽象耦合:因此无论是增加观察者还是增加被观察者,都十分容易。

2.2 可以经过扩展,从而建立一条链状的触发机制。

3 缺点

3.1 Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。这时可以考虑采用异步的方式。(多线程呗)

3.2 多级触发时的效率更让人担忧,设计时需考虑。(就是上面所说的链状触发)

4 应用场景

4.1 使用关联行为的场景。注意是可拆分的关联,而非组合。

4.2 事件多级触发场景。

4.3 跨系统的消息交换场景,如消息队列的处理机制。(如kkPlayer的全局快捷键)

5 注意事项

5.1 广播链的问题:在一个观察者模式中最多出现一个对象既是观察者又是被观察者,消息转发最多一两次,还比较好控制。

5.2 异步处理问题:需要考虑线程安全和队列的问题。(可以参考Message Queue

6 扩展

6.1 Java世界中观察者模式

在前面的通用类图及其源代码实现中,通过观察你也许会发现,抽象的被观察者subject仅帮我们关联了观察者和确定了通知机制;抽象的观察者仅确立了被通知的方法。

这些完全可以单独抽象出一个联络类,作为观察者或被观察者的职责,这样就很非常符合单一职责原则。

幸运的是,Java从天始诞生就提供了一个可扩展的父类,即Java.util.Observable,这个类专用于让别人去触发,Java.util.Observer接口则专注于对观察者通知。

此外,还应关注Java.util.Observer接口丰富的方法,可以动态的添加/删除观察者。

6.2 项目中真实的观察者模式:

因为前面讲解的都是太标准的模式,在系统设计中会对观察者模式进行改造或改装,主要是下面三方面。

A。观察者与被观察者之间的消息沟通:被观察者状态改变时会触发观察者的一个行为,同时会传递一个消息给观察者,在实际中一般的做法是:观察者中的update方法接受两个参数,一个是被观察者,一个是DTOData Transfer Object,数据传输对象),DTO一般是一个纯JavaBean,由被观察者生成,由观察者消费。(若远程,则以XML格式传递)

B。观察者响应方式:观察者是个比较复杂的逻辑,要接受被观察者传递过来的信息,同时还要对他们进行逻辑处理,如果一个观察者对应多个被观察者,则需要考虑性能。备选方法两个:一是多线程,一是缓存技术。

C。被观察者尽量自己做主:不要把消息传到观察者时才判断是否需要消费。

应用举例:

文件系统:当在目录下新建一个文件,这个动作会同时通知目录管理器增加该目录,并通知磁盘管理器减少1KB的空间,也就是“文件”是一个被观察者,“目录管理器”则是观察者。

猫鼠游戏:猫叫一声,惊动了鼠;

广播收音机:电台在广播,收音机就能收听。

7 范例

7.1 标准的“观察者模式”可以参考通用源码

7.2 这里使用原书例子,使用java提供的观察者服务类。

类图如下:

设计模式之16观察者模式(笔记)_第2张图片

需要注意的是,源码中有模拟处理耗时的情况,当然默认并没有多线程支持。。。

源代码如下:(作者原书例)

import java.util.Observable;

/**
 * @author cbf4Life [email protected] I'm glad to share my knowledge with you
 *         all. 韩非子,李斯的师弟,韩国的重要人物
 */
public class HanFeiZi extends Observable {
	// 韩非子要吃饭了
	public void haveBreakfast() {
		System.out.println("韩非子:开始吃饭了...");
		// 通知所有的观察者
		super.setChanged();
		super.notifyObservers("韩非子在吃饭");
	}

	// 韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
	public void haveFun() {
		System.out.println("韩非子:开始娱乐了...");
		super.setChanged();
		this.notifyObservers("韩非子在娱乐");
	}
}

public class LiSi implements Observer {
	// 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
	public void update(Observable observable, Object obj) {
		System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
		this.reportToQiShiHuang(obj.toString());
		System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
		try {
			System.out.println("我开始休眠 " + System.currentTimeMillis());
			Thread.sleep(3000);
			System.out.println("我起来了 " + System.currentTimeMillis());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 汇报给秦始皇
	private void reportToQiShiHuang(String reportContext) {
		System.out.println("李斯:报告,秦老板!韩非子有活动了--->" + reportContext);
	}
}

public class LiuSi implements Observer {
	// 刘斯,观察到韩非子活动后,自己也做一定得事情
	public void update(Observable observable, Object obj) {
		System.out.println("刘斯:观察到韩非子活动,开始动作了...");
		this.happy(obj.toString());
		System.out.println("刘斯:真被乐死了\n");
	}

	// 一看韩非子有变化,他就快乐
	private void happy(String context) {
		System.out.println("刘斯:因为" + context + ",——所以我快乐呀!");
	}
}

public class WangSi implements Observer {
	// 王斯,看到李斯有活动,自己就受不了
	public void update(Observable observable, Object obj) {
		System.out.println("Observable = " + observable.getClass()
				+ " , obj = " + obj);
		System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
		this.cry(obj.toString());
		System.out.println("王斯:真真的哭死了...\n");
	}

	// 一看李斯有活动,就哭,痛哭
	private void cry(String context) {
		System.out.println("王斯:因为" + context + ",——所以我悲伤呀!");
	}
}

public class Client {
	public static void main(String[] args) {
		// 三个观察者产生出来
		Observer liSi = new LiSi();
		Observer wangSi = new WangSi();
		Observer liuSi = new LiuSi();
		// 定义出韩非子
		HanFeiZi hanFeiZi = new HanFeiZi();
		// 我们后人根据历史,描述这个场景,有三个人在观察韩非子
		hanFeiZi.addObserver(wangSi);
		hanFeiZi.addObserver(liuSi);
		hanFeiZi.addObserver(liSi);
		// 然后这里我们看看韩非子在干什么
		hanFeiZi.haveBreakfast();
	}
}

测试结果:

韩非子:开始吃饭了...

李斯:观察到李斯活动,开始向老板汇报了...

李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭

李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...

我开始休眠 1337226592828

我起来了 1337226595828

刘斯:观察到韩非子活动,开始动作了...

刘斯:因为韩非子在吃饭,——所以我快乐呀!

刘斯:真被乐死了

Observable = class _16_Observer.HanFeiZi , obj = 韩非子在吃饭

王斯:观察到韩非子活动,自己也开始活动了...

王斯:因为韩非子在吃饭,——所以我悲伤呀!

王斯:真真的哭死了...

你可能感兴趣的:(设计模式,多线程,String,object,活动,Class)