title: 设计模式-观察者模式
author: Xoni
abbrlink: 4eed4967
date: 2022-09-07
cover: ‘https://blog-1259153703.cos.ap-nanjing.myqcloud.com/images/20210806165241.jpg’
tags:
- 设计模式
categories: - 设计模式
前言:
Spring 框架中都用到了哪些设计模式?
Spring 框架中使用到了大量的设计模式,下面列举了比较有代表性的:
- 工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
- 代理设计模式 : Spring AOP 功能时与使用JDK的动态代理
- 单例设计模式 : Spring 中的 Bean 默认都是单例的。
- 模板方法模式 : 用来解决代码重复的问题,Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
- 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
- ……
单例设计模式在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。使用单例模式的好处:
- 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
- 由于 new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
代理设计模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。在现实生活中,这种情形非常的常见,比如请一个律师代理来打官司
模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。
观察者模式是一种对象行为型模式。它表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应。Spring事件驱动模型就是观察者模式很经典的一个应用。Spring
事件驱动模型非常有用,在很多场景都可以解耦我们的代码。比如我们每次添加商品的时候都需要重新更新商品索引,这个时候就可以利用观察者模式来解决这个问题。适配器模式 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。
装饰者模式可以动态地给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,就可以使用装饰者模式
什么是观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者角色
- 抽象被观察者角色:定义了动态增加、删除以及通知观察者对象的方法,职责就是管理和通知观察者。持有观察者对象的集合。
- **具体被观察者角色:**一般继承抽象被观察者,实现自己本身的业务逻辑,当状态发生改变时发起通知。
- **抽象观察者角色:**提供一个接口,定义了观察者收到通知时更新自己的方法。
- **具体观察者角色:**实现抽象观察者接口,处理不同具体观察者的不同业务逻辑。
优缺点
优点
- 观察者和被观察者是抽象耦合的。
- 建立一套触发机制。
- 对象之间的保持高度的协作。当被观察者发生变化时,所有被观察者都会通知到,然后做出相应的动作。
缺点
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
- 如果观察者太多,被观察者通知观察者消耗的时间很多,影响系统的性能。
- 当观察者集合中的某一观察者错误时就会导致系统卡壳,因此一般会采用异步方式。
适用场景
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
实现
灰太狼具有被观察者属性,喜洋洋这些羊咩咩一直都在观察者灰太狼,所以羊咩咩们是观察者。OK,角色确定了,看看具体是怎么实现的…
抽象被观察者
public abstract class Subject {
/**
* 观察者对象的集合
*/
private List<Observer> observerList = new ArrayList<>();
/**
* 登记观察者
*
* @param observer
*/
public void attach(Observer observer) {
observerList.add(observer);
System.out.println("增加了观察者:" + observer.getName());
}
/**
* 删除观察者
*
* @param observer
*/
public void dettach(Observer observer) {
observerList.remove(observer);
System.out.println("删除了观察者:" + observer.getName());
}
/**
* 通知所有观察者
*/
public void notifyObserver() {
for (Observer observer : observerList) {
observer.update("灰太狼要搞事情了");
}
}
}
创建被观察者对象
灰太狼是具体被观察者,继承抽象被观察者
public class Wolf extends Subject {
public void invade(){
System.out.println("灰太狼:我要搞事情了");
// 通知所有观察者
notifyObserver();
}
}
抽象观察者
public interface Observer {
String getName();
/**
* 通知更新方法
*
* @param msg
*/
public void update(String msg);
}
创建观察者对象
喜羊羊是具体观察者
public class PleasantSheep implements Observer{
@Override
public String getName() {
return "喜羊羊";
}
/**
* 具体业务逻辑
*/
@Override
public void update(String msg) {
System.out.println("喜羊羊收到通知:" + msg);
}
}
测试
接下来看客户端如何把观察者模式跑起来
public class Client {
public static void main(String[] args) {
// 灰太狼--被观察者
Wolf wolf = new Wolf();
// 喜羊羊--观察者
Observer pleasantSheep = new PleasantSheep();
// 登记观察者
wolf.attach(pleasantSheep);
// 灰太狼入侵
wolf.invade();
}
}
输出
增加了观察者:喜羊羊
灰太狼:我要搞事情了
喜羊羊收到通知:灰太狼要搞事情了
总结
在Java中已经提供了Observable类以及一个Observer接口,也就是说Java已经实现了观察者模式的定义