共计 4996 个字符,预计需要花费 13 分钟才能阅读完成。
文章收录在 GitHub JavaKeeper,N 线互联网开发必备技能兵器谱
在软件系统中常常会有这样的需要:如果一个对象的状态产生扭转,某些与它相干的对象也要随之做出相应的变动。
- 微信公众号,如果一个用户订阅了某个公众号,那么便会收到公众号发来的音讯,那么,公众号就是『被观察者』,而用户就是『观察者』
- 气象站能够将每天预测到的温度、湿度、气压等以布告的模式公布给各种第三方网站,如果天气数据有更新,要可能实时的告诉给第三方,这里的气象局就是『被观察者』,第三方网站就是『观察者』
- MVC 模式中的模型与视图的关系也属于察看与被察看
观察者模式是应用频率较高的设计模式之一。
观察者模式蕴含察看指标和观察者两类对象,一个指标能够有任意数目的与之相依赖的观察者,一旦察看指标的状态产生扭转,所有的观察者都将失去告诉。
定义
观察者模式(Observer Pattern):定义对象间一种一对多的依赖关系,使得当每一个对象扭转状态,则所有依赖于它的对象都会失去告诉并自动更新。
观察者模式是一种 对象行为型模式。
观察者模式的别名包含公布 - 订阅(Publish/Subscribe)模式、模型 - 视图(Model/View)模式、源 - 监听器(Source/Listener)模式或隶属者(Dependents)模式。
细究的话,公布订阅和观察者有些不同,能够了解成公布订阅模式属于狭义上的观察者模式。
角色
- Subject(指标):被观察者,它是指被察看的对象。从类图中能够看到,类中有一个用来寄存观察者对象的 Vector 容器(之所以应用 Vector 而不应用 List,是因为多线程操作时,Vector 在是平安的,而 List 则是不平安的),这个 Vector 容器是被观察者类的外围,另外还有三个办法:attach 办法是向这个容器中增加观察者对象;detach 办法是从容器中移除观察者对象;notify 办法是顺次调用观察者对象的对应办法。这个角色能够是接口,也能够是抽象类或者具体的类,因为很多状况下会与其余的模式混用,所以应用抽象类的状况比拟多。
- ConcreteSubject(具体指标):具体指标是指标类的子类,通常它蕴含常常产生扭转的数据,当它的状态产生扭转时,向它的各个观察者发出通知。同时它还实现了在指标类中定义的形象业务逻辑办法(如果有的话)。如果毋庸扩大指标类,则具体指标类能够省略。
-
Observer(观察者):观察者将对察看指标的扭转做出反馈,观察者个别定义为 接口 ,该接口申明了更新数据的办法
update()
,因而又称为 形象观察者。 - ConcreteObserver(具体观察者):在具体观察者中保护一个指向具体指标对象的援用,它存储具体观察者的无关状态,这些状态须要和具体指标的状态保持一致;它实现了在形象观察者 Observer 中定义的 update()办法。通常在实现时,能够调用具体指标类的 attach() 办法将本人增加到指标类的汇合中或通过 detach() 办法将本人从指标类的汇合中删除。
类图
再记录下 UML 类图的注意事项,这里我的 Subject 是 形象办法 ,所以用 斜体,形象办法也要用斜体,具体的各种箭头意义,我之前也总结过《设计模式前传——学设计模式前你要晓得这些》(被网上各种帖子毒害过的本人,认真记录~~~)。
实例
1、定义观察者接口
interface Observer {public void update();
}
2、定义被观察者
abstract class Subject {private Vector<Observer> obs = new Vector();
public void addObserver(Observer obs){this.obs.add(obs);
}
public void delObserver(Observer obs){this.obs.remove(obs);
}
protected void notifyObserver(){for(Observer o: obs){o.update();
}
}
public abstract void doSomething();}
3、具体的被观察者
class ConcreteSubject extends Subject {public void doSomething(){System.out.println("被观察者事件产生扭转");
this.notifyObserver();}
}
4、具体的被观察者
class ConcreteObserver1 implements Observer {public void update() {System.out.println("观察者 1 收到信息,并进行解决");
}
}
class ConcreteObserver2 implements Observer {public void update() {System.out.println("观察者 2 收到信息,并进行解决");
}
}
5、客户端
public class Client {public static void main(String[] args){Subject sub = new ConcreteSubject();
sub.addObserver(new ConcreteObserver1()); // 增加观察者 1
sub.addObserver(new ConcreteObserver2()); // 增加观察者 2
sub.doSomething();}
}
输入
被观察者事件产生扭转
观察者 1 收到信息,并进行解决
观察者 2 收到信息,并进行解决
通过运行后果能够看到,咱们只调用了 Subject
的办法,但同时两个观察者的相干办法都被调用了。认真看一下代码,其实很简略,就是在 Subject
类中关联一下 Observer
类,并且在 doSomething()
办法中遍历一下 Observer
的 update()
办法就行了。
优缺点
长处
- 升高了指标与观察者之间的耦合关系,两者之间是形象耦合关系
- 指标与观察者之间建设了一套触发机制
- 反对播送通信
- 合乎“开闭准则”的要求
毛病
- 指标与观察者之间的依赖关系并没有齐全解除,而且有可能呈现循环援用
- 当观察者对象很多时,告诉的发布会破费很多工夫,影响程序的效率
利用
JDK 中的观察者模式
观察者模式在 Java 语言中的位置十分重要。在 JDK 的 java.util 包中,提供了 Observable 类以及 Observer 接口,它们形成了 JDK 对观察者模式的反对(能够去查看下源码,写的比拟谨严)。but,在 Java9 被弃用了。
Spring 中的观察者模式
Spring 事件驱动模型也是观察者模式很经典的利用。就是咱们常见的我的项目中最常见的事件监听器。
1. Spring 中观察者模式的四个角色
-
事件:ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都须要继承 ApplicationEvent, 并且通过 source 失去事件源。
Spring 也为咱们提供了很多内置事件,
ContextRefreshedEvent
、ContextStartedEvent
、ContextStoppedEvent
、ContextClosedEvent
、RequestHandledEvent
。 - 事件监听:ApplicationListener,也就是观察者,继承自 jdk 的 EventListener,该类中只有一个办法 onApplicationEvent。当监听的事件产生后该办法会被执行。
-
事件源:ApplicationContext,
ApplicationContext
是 Spring 中的外围容器,在事件监听中 ApplicationContext 能够作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在ApplicationEventPublisher
中定义了事件公布的办法:publishEvent(Object event)
- 事件治理:ApplicationEventMulticaster,用于事件监听器的注册和事件的播送。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 公布的 Event 播送给它的监听器列表。
2. coding~~
1、定义事件
public class MyEvent extends ApplicationEvent {public MyEvent(Object source) {super(source);
System.out.println("my Event");
}
}
2、实现事件监听器
@Component
class MyListenerA implements ApplicationListener<MyEvent> {public void onApplicationEvent(MyEvent AyEvent) {System.out.println("ListenerA received");
}
}
@Component
class MyListenerB implements ApplicationListener<MyEvent> {public void onApplicationEvent(MyEvent AyEvent) {System.out.println("ListenerB received");
}
}
3、事件发布者
@Component
public class MyPublisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext=applicationContext;}
public void publishEvent(ApplicationEvent event){System.out.println("publish event");
applicationContext.publishEvent(event);
}
}
4、测试,先用注解形式将 MyPublisher 注入 Spring
@Configuration
@ComponentScan
public class AppConfig {@Bean(name = "myPublisher")
public MyPublisher myPublisher(){return new MyPublisher();
}
}
public class Client {
@Test
public void main() {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyPublisher myPublisher = (MyPublisher) context.getBean("myPublisher");
myPublisher.publishEvent(new MyEvent(this));
}
}
5、输入
my Event
publish event
ListenerA received
ListenerB received
瞎扯
设计模式真的只是一种设计思维,不须要非得有多个观察者才能够用观察者模式,只有一个观察者,我也要用。
再举个栗子,我是做广告投放的嘛(广告投放的商品文件个别为 xml),如果我的广告位有些闲暇流量,这我得利用起来呀,所以我就从淘宝客或者拼夕夕的多多客上通过凋谢的 API 获取一些,这个时候我也能够用观察者模式,每次申请 10 万条商品,我就生成一个新的商品文件,这个时候我也能够用观察者模式,获取商品的类是被观察者,写商品文件的是观察者,当商品够 10 万条了,就告诉观察者从新写到一个新的文件。
大佬可能觉这么实现有点吃力,不必设计模式也好,或者用音讯队列也好,其实都只是一种伎俩,抉择适宜本人业务的,开心就好。
参考
https://design-patterns.readt…
https://www.cnblogs.com/jmcui…