前言

在上一篇中我们学习了行为型模式的备忘录模式(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 list;

private String anime;

public Bangumi(String anime) {

this.anime = anime;

list = new ArrayList();

}

@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";

Stringbingguo = "冰菓";

Stringfate = "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-study是本人在学习Java过程中记录的一些代码,也包括之前博文中使用的代码。如果感觉不错,希望顺手给个start,当然如果有不足,也希望提出。

github地址: https://github.com/xuwujing/java-study

java观察者模式异步notify_Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式...相关推荐

  1. Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式

    前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...

  2. Java进阶篇设计模式之十三——观察者模式和空对象模式

    简介 观察者模式又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependents)模式. ...

  3. java bs设计模式_Java进阶篇设计模式之十三

    前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...

  4. Java进阶篇设计模式之一 ----- 单例模式

    前言 在刚学编程没多久就听说过设计模式的大名,不过由于当时还是个彻彻底底的菜鸟,并没有去触碰.直到在开始工作中对简单的业务代码较为熟悉之后,才正式的接触设计模式.当时最早接触的设计模式是工厂模式,不过 ...

  5. Java学习历程之----进阶篇(十一)

    千禧难题之二:      霍奇猜想:在非奇异复射影代数簇上, 任一霍奇类是代数闭链类的有理线性组合.它是关于非奇异复代数簇的代数拓扑和它由定义子簇的多项式方程所表述的几何的关联的猜想.由威廉·瓦伦斯· ...

  6. 设计模式笔记二十二:空对象模式

    原文:http://www.runoob.com/design-pattern/ 少许个人理解,如有错误请指出.欢迎一起讨论. 在空对象模式(Null Object Pattern)中,一个空对象取代 ...

  7. php7 空对象,PHP设计模式之空对象模式(Null Object)代码实例大全(26)

    目的 空对象模式不属于 GoF 设计模式,但是它作为一种经常出现的套路足以被视为设计模式.它具有如下优点: 客户端代码简单 可以减少报空指针异常的几率 测试用例不需要考虑太多条件 返回一个对象或 nu ...

  8. 【备战春招/秋招系列】美团Java面经总结进阶篇 (附详解答案)

    <!-- MarkdownTOC --> 一 消息队列MQ的套路 1.1 介绍一下消息队列MQ的应用场景/使用消息队列的好处 ①.通过异步处理提高系统性能 ②.降低系统耦合性 1.2 那么 ...

  9. 被遗忘的设计模式——空对象模式(转载)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qiumengchen12/articl ...

最新文章

  1. 一个虚函数和虚继承的问题。
  2. 重装电脑后遇到的问题,其他设备,未知设备
  3. 基于OpenCV的显著图绘制
  4. cursor_sharing用法
  5. C语言实现hashset算法(附完整源码)
  6. 用jsp实现登录界面
  7. 不会英语能学oracle吗,英语非常差该怎么学啊?
  8. 什么是光纤通道交换机(FC SWITCH)
  9. 关于关闭office 2010中的OSPPSVC服务的方法
  10. android 字体颜色选择,Android中颜色选择器和改变字体颜色的实例教程
  11. UVA 188 Perfect Hash
  12. 501.二叉搜索树中的众数
  13. FlashDevelop打包IOS应用教程
  14. JSP中application的用法
  15. Vue角色的权限管理
  16. MyBatis 基础知识
  17. jQuery控制网页字体大小
  18. 小黄鸡 php,Simsimi (小黄鸡) API接口(PHP)公布,小黄鸡API接口非官方PHP版本来啦...
  19. 如何更改oracle_sid,Oracle SID修改方式的详解
  20. java 由日期计算星期几_java计算日期是星期几

热门文章

  1. ubuntu下安装大恒相机驱动并调用程序采集图像
  2. ios开发中常用的几种辅助方法
  3. 如何下载(高程数据)并生成等高线?
  4. m无线通信信道matlab仿真,包括自由空间损耗模型,Okumura-Hata模型,COST231 Hata模型,SUI信道模型
  5. 插层熔喷非织造材料的性能控制研究-数学建模大赛华数杯C题 -(分享全部解题过程)
  6. Ubuntu 20.04安装wps程序步骤,史上最简洁实用
  7. 如何区分室外网线和室内网线?
  8. Log4j、Logback的使用以及日志门面模式(外观模式)
  9. 脚本批量执行Redis命令
  10. wpf教程-环境搭建