前言
本来没想写前言的,感觉就是一堆废话。那就当废话浏览一下吧,只是提醒一下自己一些注意的东西。
在许多博客当中看到把观察者模式又称呼为发布-订阅模式。其实我感觉两者确实很相似,但还是有一丢丢的区别:
在发布-订阅模式中消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者(订阅者)。
举个例子:比如微信公众号。意思就是发布者和订阅者不知道对方的存在,需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。(这段从网上找的,大家可以从网上详细了解发布-订阅模式)这里就不跑题了。
一、简介
1、属于行为型模式:这些设计模式特别关注对象之间的通信。
2、当对象间存在一对多关系时,则使用观察者模式。比如,当一个对象被修改时,则会自动通知它的依赖对象。
3、意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
4、主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
5、何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
6、使用场景:
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
(3)一个对象必须通知其他对象,而并不知道这些对象是谁。
(4)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
7、注意事项:
(1)JAVA 中已经有了对观察者模式的支持类。
(2)避免循环引用。
(3)如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
二、实现步骤
(1)实现方式:
实现观察者模式有很多形式,比较直观的一种是使用一种“注册——通知——注销”的形式。
比如Android中的广播(不懂android也没关系,把它想象成学校的广播就成,具体看下面例子)
观察者模式主要角色
抽象观察者:描述观察者的公共接口(收到消息的方法),也可以定义为接口(interface)。
具体观察者:描述具体观察者并对观察目标的改变做出反应。
抽象被观察者(目标):是指被观察的对象,描述被观察者的公共接口(比如通知、注册、注销等),也可以定义为接口
具体被观察者(具体目标):描述具体的被观察者,与观察者建立联系。当状态发生变化时,通知观察者。
(2)举例:
这里举个例子:智商有点不够用,想不到好的例子,这里就模拟Android四大组件之一的广播实现方式。
首先我们要知道Android广播的大致实现流程:创建接收者(具体观察者)继承广播(抽象观察者),然后注册广播(绑定观察者,建立联系)。广播发送者(具体的被观察者)发送广播通知。最后是注销广播(解除绑定,断开联系)。
看起来有点复杂,换种思路简单的想象成学校发出广播通知学生放学即可。
(3)步骤简化版:
1、创建具体被观察者(学校广播)继承抽象 被观察者
2、创建具体观察者(学生)继承抽象观察者
3、绑定联系
4、发送通知
哈哈 ~!相信到这里大家可以自己手动了实现了,下面贴出具体代码。
三、代码实现
抽象 被观察者:Observable.java;具体 被观察者:SchoolsBroadcast.java
/**
* Observable.java
* 抽象被观察者
*/
abstract class Observable {
//发送广播
abstract void sendBroadcast(String message);
}
/**
* SchoolsBroadcast.java
* 具体的 被观察者(学校广播)
*/
public class SchoolsBroadcast extends Observable{
//用来存储观察者
private List<Observer> observers = new ArrayList<Observer>();
@Override
void sendBroadcast(String message) {
System.out.println("学校发出通知:"+message);
for(Observer ob:observers) {
ob.receive(message);
}
}
//绑定观察者(可以移动到抽象被观察者中)
public void registerReceiver(Observer observer) {
observers.add(observer);
}
//解绑观察者(可以移动到抽象被观察者中)
public void unRegisterReceiver(Observer observer) {
if(observers.contains(observer)) {
observers.remove(observer);
}
}
}
抽象 观察者:Observer.java;具体 观察者:StudentA.java、StudentB.java、StudentC.java
/**
* Observer.java
* 抽象观察者
*/
abstract class Observer {
//收到通知
abstract void receive(String message);
}
/**
* StudentA.java
* 具体的观察者(学生A)
*/
public class StudentA extends Observer{
@Override
void receive(String message) {
System.out.println("学生A收到消息:"+message);
}
}
/**
* StudentB.java
* 具体的观察者(学生B)
*/
public class StudentB extends Observer{
@Override
void receive(String message) {
System.out.println("学生B收到消息:"+message);
}
}
/**
* StudentC.java
* 具体的观察者(学生C)
*/
public class StudentC extends Observer{
@Override
void receive(String message) {
System.out.println("学生C收到消息:"+message);
}
}
测试类:Test.java
/**
* Test.java
* 测试类
*/
public class Test {
public static void main(String[] args) {
//创建被观察者(学校广播)
SchoolsBroadcast schoolsBroadcast = new SchoolsBroadcast();
//创建观察者(学生)
StudentA studentA = new StudentA();
//绑定观察者 建立联系
schoolsBroadcast.registerReceiver(studentA);
schoolsBroadcast.registerReceiver(new StudentB());
schoolsBroadcast.registerReceiver(new StudentC());
//被观察者(学校广播)发出通知
schoolsBroadcast.sendBroadcast("放学");
System.out.println("=====================");
//解绑观察者(学生A)
schoolsBroadcast.unRegisterReceiver(studentA);
//被观察者(学校广播)发出通知
schoolsBroadcast.sendBroadcast("学生A请假");
}
}
例子很简单、容易忘的可以多写几遍,很容易记住。当然面试时还是贼好用滴!!!
四、总结
优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。.
3、符合“开闭原则”的要求。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
五、Demo地址
https://github.com/DayorNight/DesignPattern
六、参考文档
http://www.runoob.com/design-pattern/observer-pattern.html
七、内容推荐
CSDN:https://blog.csdn.net/cs_lwb/article/details/84189465
相关文章:
如果你觉得我写的不错或者对您有所帮助的话。不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈!!
您的每个举动都是对我莫大的支持