观察者模式
高中的时候,班里有个两个同学太困了,就钻到桌子地下睡觉,结果被班主任抓包,打断了跟拖把,虽然面临高考还能这么淡定也是个人才。
怎么回事呢?
刘某被老师长期罚坐在门外上课!于是郑某每天帮刘某买饭,刘某成为了郑某的眼线,看到班主任来就把他叫起来。这样相安无事过了几天,然而事实证明刘某是靠不住的,今天刘某也睡着了,然后班主任亲自叫醒了刘某,然后带着刘某叫醒了班里熟睡的郑某,这样刘郑二人双双被打,直到拖把杆被打断。最后一个体育生郑dh跑进了中国矿业大学,另一个睡着了没离开又复读了一年!
这不就是观察者模式吗?
你可能认为刘某是观察者,其实郑某才是观察者,他观察刘某的动态,然后做出反馈,有点违背我们平时的理解。啪~ 啪 ~ 啪~!请看下文!
观察者模式又叫做:
- 模型-视图(Model/View)模式
- 源-监听器(Source/Listener)模式
- 从属者(Dependents)模式
脑海中立刻闪现了《Head First设计模式》里讲的:
Publishers + Subscribers = Observer Pattern
他还应该叫发布订阅模式?但是确实不是,之前我混为一谈确实不对!这篇文章比较详细的讲解他们两个区别,并给出了源代码及实例,有兴趣的可以看一下。
发布者订阅者模式到底是个啥?看这!
意图:
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其它的对象做出相应的改变。做到这一点的设计方案有很多,是为了使系统能够易于复用,应该选择低耦合度的设计方案为了使系统能够易于复用,应该选择低耦合度的设计方案。
减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作( Collaboration)。观察者模式是满足这一要求的各种设计方案中最重要的一种。
主要解决:
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:
使用面向对象技术,可以将这种依赖关系弱化,从而降低耦合性,避免循环观察形成死锁。
关键代码:
在抽象类里有一个 ArrayList 存放观察者们。
UML图:
观察者模式(Observer), 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。UML结构图如下:
优点:
- 1.观察者和被观察者是抽象耦合的。
- 2.建立一套触发机制:
- 观察者模式实现了动态联动
- 观察者模式支持广播通信。被观察者会向所有的登记过的观察者发出通知。
缺点:
- 可能存在无用的操作:
由于采用广播模式,不管观察者是否需要,都会被通知调用Update()方法! - 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项
目标和观察者之间的关系:
- 典型的一对多的关系
单向依赖:
- 观察者依赖于目标
触发通知的时机:
- 完成了状态维护后触发
相互观察:
- A、B观察C,B、C观察A(两套观察模式)
- 注意死循环
通知的顺序
- 绝对不要依赖于通知的顺序,多个观察者之间的功能是平行的,相互不应该有先后的依赖关系
实现:
抽象主题(Subject)角色:
- 一个目标可以被多个观察者观察
- 目标提供对观察者注册和退订的维护
- 当目标的状态发生变化时,目标负责通知所有注册的、有效地观察者
首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
public class Subject {
//观察者数组
private Vector<Observer> oVector = new Vector<>();
//增加一个观察者
public void addObserver(Observer observer) {
this.oVector.add(observer);
}
//删除一个观察者
public void deleteObserver(Observer observer) {
this.oVector.remove(observer);
}
//通知所有观察者
public void notifyObserver() {
for(Observer observer : this.oVector) {
observer.update();
}
}
}
抽象观察者Observer
- 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。
- 抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法。
public interface Observer {
//更新
public void update();
}
具体主题 (ConcreteSubject)
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)具体主题角色通常用一个具体子类实现。
public class ConcreteSubject extends Subject {
//具体业务
public void doSomething() {
//...
super.notifyObserver();
}
}
具体观察者(ConcreteObserver)
具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("收到消息,进行处理");
}
}
Client客户端
首先创建一个被观察者,然后定义一个观察者,将该被观察者添加到该观察者的观察者数组中,进行测试。
public class Client {
public static void main(String[] args) {
//创建一个主题
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer observer = new ConcreteObserver();
//观察
subject.addObserver(observer);
//开始活动
subject.doSomething();
}
}
观察者模式的应用
在实际应用中,抽象观察者Observer在C#和java中都已经被实现,我们可以直接使用。
- Java中的观察者模式
java.util.Observable,实现了大部分我们需要的目标的功能,还有一个接口Observer,其中定义了update方法,就是观察者的接口。在java9以后被弃用,弃用原因,后续将会填坑通过监听来重新实现观察者模式! - C#的Observer模式
实际上在C#中实现Observer模式没有这么辛苦,.NET
中提供了Delegate
与Event
机制,我们可以利用这种机制简化Observer模式。基于.NET简化的观察者模式。
举个例子:
那天博主的股票上市了,你们买了我的股票,我的股票涨了跌了得通知你们,我们就按照这个写一个。
package 观察者模式;
public interface Observer {
public void update(Object c);
}
package 观察者模式;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
public class Subject {
private Vector<Observer> oVector;
public Subject() {
oVector=new Vector<Observer>();
}
public void addObserver(Observer observer) {
this.oVector.add(observer);
}
public void deleteObserver(Observer observer) {
this.oVector.remove(observer);
}
public void notifyObserver() {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = new Date( );
sf.format(d);
for (Observer observer : this.oVector) {
observer.update(d);
}
}
}
package 观察者模式;
public class Share_Observer implements Observer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void update(Object c) {
System.out.println(name+"已更新股票数据"+c);
}
}
package 观察者模式;
public class Shares_Subject extends Subject {
public void RealTime_Update() {
super.notifyObserver();
}
}
package 观察者模式;
public class Client {
public static void main(String[] args) {
Shares_Subject subject = new Shares_Subject();
Share_Observer observer = new Share_Observer();
observer.setName("张俊浩的A股");
subject.addObserver(observer);
subject.RealTime_Update();
}
}
写在最后:
我叫风骨散人,名字的意思是我多想可以不低头的自由生活,可现实却不是这样。家境贫寒,总得向这个世界低头,所以我一直在奋斗,想改变我的命运
给亲人好的生活,希望同样被生活绑架的你
可以通过自己的努力改变现状,深知成年人的世界里没有容易二字。目前是一名在校大学生,预计考研,热爱编程,热爱技术,喜欢分享,知识无界,希望我的分享可以帮到你!
如果有什么想看的,可以私信我,如果在能力范围内,我会发布相应的博文!
谢谢大家的阅读!😘