Java设计模式之十三 ---- 观察者模式和空对象模式
前言
在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern)。本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer Pattern)和空对象模式(NullObject Pattern)。
观察者模式
简介
观察者模式又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。。
其主要目的是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式主要由这四个角色组成,抽象主题角色(Subject)、具体主题角色(ConcreteSubject)、抽象观察者角色(Observer)和具体观察者角色(ConcreteObserver)。
- 抽象主题角色(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题角色(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
- 抽象观察者角色(Observer):主要是负责从备忘录对象中恢复对象的状态。
示例图如下:

我们这里用一个示例来进行说明吧。
我们在视频网站进行看剧追番的时候,一般会有一个订阅功能,如果对某个番剧点了订阅,那么该番剧在更新的时候会向订阅该番剧的用户推送已经更新的消息,如果取消了订阅或者没有订阅,那么用户便不会收到该消息。
那么我们可以根据这个场景来使用备忘录模式来进行开发。
首先定义一个抽象主题, 将观察者(订阅者)聚集起来,可以进行新增、删除和通知,这里就可以当做番剧。
代码如下:
interface BangumiSubject{
void toThem(UserObserver user);
void callOff(UserObserver user);
void notifyUser();
}
然后再定义一个抽象观察者,有一个主要的方法update,主要是在得到通知时进行更新,这里就可以当做是用户。
代码如下:
interface UserObserver{
void update(String bangumi);
String getName();
}
然后再定义一个具体主题,实现了抽象主题(BangumiSubject)接口的方法,同时通过一个List集合保存观察者的信息,当需要通知观察者的时候,遍历通知即可。
代码如下:
class Bangumi implements BangumiSubject {
private List<UserObserver> list;
private String anime;
public Bangumi(String anime) {
this.anime = anime;
list = new ArrayList<UserObserver>();
}
@Override
public void toThem(UserObserver user) {
System.out.println("用户"+user.getName()+"订阅了"+anime+"!");
list.add(user);
}
@Override
public void callOff(UserObserver user) {
if(!list.isEmpty())
System.out.println("用户"+user.getName()+"取消订阅"+anime+"!");
list.remove(user);
}
@Override
public void notifyUser() {
System.out.println(anime+"更新了!开始通知订阅该番剧的用户!");
list.forEach(user->
user.update(anime)
);
}
}
最后再定义了一个具体观察者,实现抽象观察者(UserObserver)接口的方法。
代码如下:
class User implements UserObserver{
private String name;
public User(String name){
this.name = name;
}
@Override
public void update(String bangumi) {
System.out.println(name+"订阅的番剧: " + bangumi+"更新啦!");
}
@Override
public String getName() {
return name;
}
}
编写好之后,那么我们来进行测试。
这里我们定义两个用户角色,张三和xuwujing,他们都订阅了<冰菓>和番剧,当番剧更新的时候,他们就会收到通知。 如果他们取消了该番剧的订阅,那么他就不会收到该番剧的通知了。
相应的测试代码如下:
public static void main(String[] args) {
String name1 ="张三";
String name2 ="xuwujing";
String bingguo = "冰菓";
String fate = "fate/zero";
BangumiSubject bs1 = new Bangumi(bingguo);
BangumiSubject bs2 = new Bangumi(fate);
UserObserver uo1 = new User(name1);
UserObserver uo2 = new User(name2);
//进行订阅
bs1.toThem(uo1);
bs1.toThem(uo2);
bs2.toThem(uo1);
bs2.toThem(uo2);
//进行通知
bs1.notifyUser();
bs2.notifyUser();
//取消订阅
bs1.callOff(uo1);
bs2.callOff(uo2);
//进行通知
bs1.notifyUser();
bs2.notifyUser();
}
输出结果:
用户张三订阅了冰菓!
用户xuwujing订阅了冰菓!
用户张三订阅了fate/zero!
用户xuwujing订阅了fate/zero!
冰菓更新了!开始通知订阅该番剧的用户!
张三订阅的番剧: 冰菓更新啦!
xuwujing订阅的番剧: 冰菓更新啦!
fate/zero更新了!开始通知订阅该番剧的用户!
张三订阅的番剧: fate/zero更新啦!
xuwujing订阅的番剧: fate/zero更新啦!
用户张三取消订阅冰菓!
用户xuwujing取消订阅fate/zero!
冰菓更新了!开始通知订阅该番剧的用户!
xuwujing订阅的番剧: 冰菓更新啦!
fate/zero更新了!开始通知订阅该番剧的用户!
张三订阅的番剧: fate/zero更新啦!
观察者模式优点:
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
观察者模式缺点
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
需要关联行为的场景;
事件需要创建一个触发链的场景,比如监控;
跨系统的消息交换场景,比如消息队列、事件总线的处理机制。
注意事项:
如果顺序执行,某一观察者错误会导致系统卡壳,建议采用异步方式。
空对象模式
简介
空对象模式(NullObject Pattern)主要是通过一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。 这样的Null 对象也可以在数据不可用的时候提供默认的行为。
其主要目的是在进行调用是不返回Null,而是返回一个空对象,防止空指针异常。
空对象模式,作为一种被基本遗忘的设计模式,但却有着不能被遗忘的作用。为什么说这么说呢,因为这种模式几乎难以见到和使用,不是它不够好用,也不是使用场景少 ,而是相比于简单的空值判断,使用它会显得比较复杂,至于为什么这么说,我们可以通过以下示例来进行说明。
假如我们要根据用户在已存的数据中进行查找相关信息,并且将它的信息给返回回来的话,那么一般我们是通过该用户的名称在数据库中进行查找,然后将数据返回,但是在数据库中进行查找时,很有可能没有该用户的信息,因此返回Null,如果稍不注意,就会出现空指针异常。这时我们一般的做法是,查询之后判断该数据是否为Null,如果为Null,就告知客户端没有这条数据,虽然这么做可以防止空指针异常,但是类似该方法过多,并且返回的信息实体为同一个的时候,我们每次都需要判断,就有点过于繁琐。那么这时我们就可以使用空对象模式来实现这方面的功能。
首先定义一个抽象角色,有获取姓名和判断是否为空的方法,这个抽象类的代码如下:
interface AbstractUser {
String getName();
boolean isNull();
}
定义好该抽象类之后,我们再来定义具体实现类。这里定义两实现个类,一个表示是真实的用户,返回真实的姓名,一个是不存在的用户,用另一种方式返回数据,可以告知客户端该用户不存在,预防空指针。
代码如下:
class RealUser implements AbstractUser {
private String name;
public RealUser(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isNull() {
return false;
}
}
class NullUser implements AbstractUser {
@Override
public String getName() {
return "user is not exist";
}
@Override
public boolean isNull() {
return true;
}
}
然后在来定义一个工厂角色,用于对客户端提供一个接口,返回查询信息。
代码如下:
class UserFactory {
public static final String[] names = { "zhangsan", "lisi", "xuwujing" };
public static AbstractUser getUser(String name) {
for (int i = 0; i < names.length; i++) {
if (names[i].equalsIgnoreCase(name)) {
return new RealUser(name);
}
}
return new NullUser();
}
}
最后再来进行测试,测试代码如下:
public static void main(String[] args) {
AbstractUser au1 = UserFactory.getUser("wangwu");
AbstractUser au2 = UserFactory.getUser("xuwujing");
System.out.println(au1.isNull());
System.out.println(au1.getName());
System.out.println(au2.isNull());
System.out.println(au2.getName());
}
输出结果:
true
user is not exist
false
xuwujing
空对象优点:
可以加强系统的稳固性,能有效防止空指针报错对整个系统的影响;
不依赖客户端便可以保证系统的稳定性;
空对象缺点:
需要编写较多的代码来实现空值的判断,从某种方面来说不划算;
使用场景:
需要大量对空值进行判断的时候;
至此,设计模式总结学习告一段落了.了解的只是基础,想要彻底深入学习好设计模式还得继续学习,在平时的工作应用中去钻研和总结!
Java设计模式之十三 ---- 观察者模式和空对象模式的更多相关文章
- Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式
前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...
- Java设计模式(十三) 别人再问你设计模式,叫他看这篇文章
原创文章,转载请务注明出处 OOP三大基本特性 封装 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类操作,对不可信的进行信息隐藏. 继承 继承是指这样一种能力,它可以使 ...
- Java设计模式百例 - 观察者模式
观察者(Observer)模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,主体对象的状态变化会通知所有观察者对象.观察者模式又叫做发布-订阅(Publish/Subscribe ...
- 被遗忘的设计模式——空对象模式(Null Object Pattern)
GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...
- 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)
设计模式 ( 十六 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...
- 设计模式:空对象模式(Null Object Pattern)
设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...
- Java设计模式之《观察者模式》及应用场景
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6513651.html 观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监 ...
- C# 设计模式之空对象模式
最近看了不少的书籍和视频等相关资料,决定自己边学习边写一下个人对设计模式的理解,如果有不对的请大家多多指正. 今天先说说我个人觉得最简单的设计模式 -- [空对象模式] 空对象模式可以减少客户端对对象 ...
- GoLang设计模式12 - 空对象模式
空对象设计模式是一种行为型设计模式,主要用于应对空对象的检查.使用这种设计模式可以避免对空对象进行检查.也就是说,在这种模式下,使用空对象不会造成异常. 空对象模式的组件包括: Entity:接口,定 ...
随机推荐
- ZOJ 1456 Minimum Transport Cost(Floyd算法求解最短路径并输出最小字典序路径)
题目链接: https://vjudge.net/problem/ZOJ-1456 These are N cities in Spring country. Between each pair of ...
- WCF返回表datatable时的解决
在WCF中有时返回值类型是一张表,就会遇到反序列化的问题.解决该问题我归纳了两种方法: 1.根据Model层的类,在服务器端将Table转化成List,在客户端再将List转化为table /// 将 ...
- SQL Where in (1,2,3,4) 换成字段一列的值
) ; , ) ) FROM r_resource WHERE id IN ( @resource) 换成 ) : , ) ) FROM r_resource )) SELECT cid,id FRO ...
- 钉钉接口:获取accessToken和打卡记录【分享】
post和get方法工具类:HttpUtils package weaver.dingtalk.utils; import com.alibaba.fastjson.JSONObject; impor ...
- 无法初始化 PowerShell 主机解决方案
Question无法初始化 PowerShell 主机.如果您的 PowerShell 执行策略设置设为 AllSigned,请先打开程序包管理器控制台以初始化该主机. --------------- ...
- 使用js从element的matrix推导transform的scale、rotate 和 translate参数
transform 网上很多都只介绍了还原角度和缩放的参数,但是没有就偏移量的计算,自己还原了一下公式的意义,进行了公式的反推,具体的推到过程就不详叙了,可以参看w3c的矩阵含义. 直接上干货. fu ...
- userDefineFunc.js
var scareMe = function(){ console.log("cynthia") scareMe = function(){ console.log("w ...
- js之搜索框
目标效果:点击搜索框,搜索框内提示信息消失,可输入搜索信息,点击搜索框外搜索框如果没提示信息或者为空时,显示搜索框提示信息,如果有搜索信息,显示搜索信息. 代码如下: <!DOCTYPE htm ...
- input上传文件个数控制
HTML: <h3>请上传[2,5]个文件</h3> <form action="" enctype="multipart/form-dat ...
- 2018-01-19 Xtext试用: 快速实现简单领域专用语言(DSL)
环境搭建 使用的Eclipse版本: Oxygen.1a Release (4.7.1a) Build id: 20171005-1200, 通过添加Xtext - Download上列出的Relea ...