观察者设计模式

1,417 阅读3分钟

大纲

image.png

什么是观察者设计模式?

观察者设计模式是一种对象行为模式,有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式、从属者模式、发布订阅模式)。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

观察者模式的组成角色

1、抽象主题(Subject):

它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供增加和删除观察者对象的接口。

2、具体主题(Concrete Subject):

将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

3、抽象观察者(Observer):

为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

4、具体观察者(Concrete Observer):

实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调

观察者模式类图如下图所示:

image.png

代码实现

//抽象主题,即抽象被观察对象
public interface Subject {

	void add(Observer observer);

	void remove(Observer observer);

	void notifyObserver();
}

//具体被观察对象
class ConcreteSubject implements Subject {

	private List<Observer> listeners = new ArrayList<>();

	@Override
	public void add(Observer observer) {
		this.listeners.add(observer);
	}

	@Override
	public void remove(Observer observer) {
		this.listeners.remove(observer);
	}

	@Override
	public void notifyObserver() {
		for (Observer listener : listeners) {
			listener.response();
		}
	}
}

//抽象观察者
interface Observer {
	void response();
}

//具体观察者
class ConcreteObserver1 implements Observer{

	@Override
	public void response() {
		System.out.println("观察者1");
	}
}

//具体观察者
class ConcreteObserver2 implements Observer{

	@Override
	public void response() {
		System.out.println("观察者2");
	}
}

class ClientTest{
	public static void main(String[] args) {
		Subject subject = new ConcreteSubject();
		ConcreteObserver1 observer1 = new ConcreteObserver1();
		ConcreteObserver2 observer2 = new ConcreteObserver2();

		subject.add(observer1);//观察者1
		subject.add(observer2);//观察者2


		subject.notifyObserver();//被观察者发生变化,主动通知观察者
	}
}

至此,基本的观察者设计模式已经实现了,但是,我们分析一下上面的代码,每次被观察对象发生改变,都会触发所有的观察者响应,假如有上百甚至上千个观察者,是否每次被观察对象发生变化,所有的观察者都要进行响应呢?

    Subject subject = new ConcreteSubject();
		ConcreteObserver1 observer1 = new ConcreteObserver1();
		ConcreteObserver9999 observer9999 = new ConcreteObserver2();
		//此处省略上万个观察者...
		subject.add(observer1);
		//...
		subject.add(observer9999);
		
		//被观察者对象发生变化啦
		subject.notifyObserver();
		//响应1
		//...
		//响应9999

进阶学习

面对上述情况,我们可以对代码进行改造,主题发生变化则发布特定的事件,监听该事件的观察者做出响应。

//抽象事件
public abstract class CustomEvent {

}
//具体事件
public class LoginCustomEvent extends CustomEvent {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

//抽象主题
public interface Subject {

	void add(Observer observer);

	void remove(Observer observer);
	
  //事件发生变化,只通知监听该事件的观察者
	void notifyObserver(CustomEvent event);
}
//具体主题
class ConcreteSubject implements Subject {

	private List<Observer> listeners = new ArrayList<>();

	@Override
	public void add(Observer observer) {
		this.listeners.add(observer);
	}

	@Override
	public void remove(Observer observer) {
		this.listeners.remove(observer);
	}

	@Override
	public void notifyObserver(CustomEvent event) {
		for (Observer listener : listeners) {
			Type[] types = listener.getClass().getGenericInterfaces();
			for (Type type : types) {
				String typeName = type.getTypeName();
				ParameterizedType parameterizedType = (ParameterizedType) type;
				Type argument = parameterizedType.getActualTypeArguments()[0];
        //找出具体观察者监听的事件名称
				String argumentTypeName = argument.getTypeName();
        //事件名称
				String eventName = event.getClass().getName();
        //如果名称相等,则表明该观察者监听该事件,实际开发可不能这么简单的判断哟
				if (argumentTypeName.equals(eventName)) {
					listener.response(event);
				}
			}
		}
	}
}

interface Observer<Event extends CustomEvent> {
	void response(Event customEvent);
}

class ConcreteObserver1 implements Observer<LoginCustomEvent> {

	@Override
	public void response(LoginCustomEvent customEvent) {
		System.out.println("触发登录事件");
		System.out.println(customEvent.getName());
	}
}

class ClientTest {
	public static void main(String[] args) {
		Subject subject = new ConcreteSubject();

		ConcreteObserver1 observer1 = new ConcreteObserver1();
		LoginCustomEvent event = new LoginCustomEvent();
		event.setName("loginEvent");
		subject.add(observer1);
		subject.notifyObserver(event);
	}
}

至此,我们实现了主题发生变化通过发布不同的事件,通知监听该事件的观察者即可,避免了无关的观察者也作出响应,提高代码的灵活度。

优缺点

观察者模式的主要的作用就是对对象解耦,将观察者和被观察者完全隔离。

1、观察者模式的优点

观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体,符合依赖倒置原则。

2、观察者模式的缺点

在应用观察者模式时需要考虑一下开发小路问题,程序中包括一个被观察者和多个被观察者,开发和调试比较复杂,而且Java中的消息的通知默认是顺序执行的,一个观察者的卡顿会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。

导致系统变得复杂,当然,引入了设计模式必然会提高系统的复杂性。

应用场景

1、当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2、当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要被改变。

3、当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,不希望这些对象是紧密耦合的。

这里举几个实际应用的场景:

用户注册成功后发送邮件给用户;

支付场景:用户购买商品时,用户支付成功之后三方会回调自身,在这个时候系统可能会有很多需要执行的逻辑 (如:更新订单状态,发送邮件通知,赠送礼品…)

文章审核:发文章通常需要我们的领导进行审核,审核通过需要怎么处理,不通过又怎么处理,都可以使用观察者设计模模式处理。

以上仅为个人学习总结,如有错误欢迎指出。

参考资料: baike.baidu.com/item/%E8%A7…