被遗忘的设计模式——空对象模式(Null Object Pattern)

时间:2022-04-22 11:10:57
GoF(*)那本《设计模式 可复用面向对象软件的基础》可谓是设计模式方面的经典之作,其中介绍的23种设计模式,
也可谓是经典中的经典。但是,设计模式的种类绝不仅仅是这23种,除此之外还有很多巧妙可爱的设计模式值得我们学习。这些
被遗忘的设计模式,也可以堪称经典之作。今天我们来一起学习被遗忘的设计模式——空对象模式(Null Object Pattern)。
一起看看这个模式会带给我们怎样的惊喜?

一、Pattern name

Provide an object as a surrogate for the lack of an object of a given type. The Null Object provides intelligent do nothing behavior, hiding the details from its collaborators.

二、Problem

任何没有实际应用场景的设计模式,都是在耍流氓。学习设计模式,不仅仅是为了领悟其精髓,更为了在实践设计当中去运用,去变通,下面我们来看看,什么情况下,这个Null Object Pattern会派上用场呢?

假设这样一个场景:

在一个图书信息查询系统中,你调用一个方法,传过去你要查找图书的ID,然后它返回给你,你要查找的图书对象,这样你就可以调用对象的方法来输出图书的信息。

我想这种场景在程序设计中还是比较常见的。下面,我们来实现以下具体的代码。

首先,我们来看一下ConcreteBook类的代码(提供构造函数和展示图书信息的show()方法。):

public class ConcreteBook {
private int ID;
private String name;
private String author; // 构造函数
public ConcreteBook(int ID, String name, String author) {
this.ID = ID;
this.name = name;
this.author = author;
} /**
*
* Description About show: <br>
* 展示图书的相关信息
*
* @version V1.0
*/
public void show() {
System.out.println(ID + "**" + name + "**" + author);
} }

我们再来看看创建图书对象的图书工厂的代码(主要提供一个获得ConcreteBook的方法):

public class BookFactory {
/**
*
* Description About getBook: <br>
* 根据ConcreteBook的ID,获取图书对象。
* @param ID 图书的ID
* @return 图书对象
* @version V1.0
*/ public ConcreteBook getBook(int ID) {
ConcreteBook book = null;
switch (ID) {
case 1:
book = new ConcreteBook(ID, "设计模式", "GoF");
break;
case 2:
book = new ConcreteBook(ID, "被遗忘的设计模式", "Null Object Pattern");
break;
default:
book = null;// 其实这个可以省略,因为初始化已经赋值为null。
break;
} return book;
}
}

最后,来看一下客户端的代码:

public class Client {

    static void main(String[] args) {
BookFactory bookFactory = new BookFactory();
ConcreteBook book = bookFactory.getBook(1);
book.show();
} }

上面三段代码很简单,我就不做详细解释了。下面,我们来运行一下,结果如下:

被遗忘的设计模式——空对象模式(Null Object Pattern)

很好,运行很顺利,这时,我们把ConcreteBook book = bookFactory.getBook(1);中的1改为2,恩,也运行成功。这时候,我们改成-1。再来运行一下,发现如下报错:

被遗忘的设计模式——空对象模式(Null Object Pattern)

空指针报错,是的,这应该是Java初学者见到最多的报错了。它提示我们第28行book.show()报错。这是为什么呢?因为我们通过bookFactory.getBook()方法获取ConcreteBook对象的时候,如果我们传入的参数,即图书的ID,属于非法值(如-1)或者不存在(如3)的话(其实这种情况是经常遇到的。),就会返回null,表示我们查找的图书信息并不存在。这时,book为null.你再调用book.show()。当然要报空指针的错误了。那怎么解决呢?

我们比较常规的做法就是在客户端加一个判断,判断是否为null。如果为null的话,就不再调用show()方法。如果不为null再调用show()方法。更改如下:

public static void main(String[] args) {
BookFactory bookFactory = new BookFactory();
ConcreteBook book = bookFactory.getBook(-1);
//判断book对象是否为null。
if (book == null) {
System.out.println("book对象为 null。");
} else {
book.show();
}
}

此时,再运行,就不会报错了。而是,输出了:book对象为null。

但是,你有没有考虑过?这样做,确实消除了报错,但是这样做真的好吗?你想如果在一段程序中有很多处调用getBook()方法或者有很多个客户端的话(比如图书馆的查询终端肯定不止一个啊),岂不是很多处都要判断book对象是否为null?这还不算坏,如果哪一处没有判断,然后报错了,很有可能导致程序没法继续运行甚至崩溃。而且,你要记住,永远都不要太相信客户端(Client),不要把整个程序的稳定性寄托在客户端身上。还有,像上面的处理方法,当获取对象为null的时候,输出的提示信息是有客户端来定制的,这样岂不是把主动权交给了客户端,而不是我们系统本身?

那究竟应该如何实现才会更加合适呢?那就要用到我们今天要讲的Null Object Pattern——一种被遗忘的设计模式

三、Solution

首先,我们来看一下Null Object Pattern的UML类图结构:

被遗忘的设计模式——空对象模式(Null Object Pattern)

这个类图结构其实还是很简单的,这里面的RealObject其实就相当于我们的ConcreteBook类,而NullObject就是我们将要增加的空对象类,而AbstractObject类就是我们要提出来的父类。我们只是在Client和AbstractObject之间增加了一个BookFactory而已。

下面,我们来改一下我们的代码:

新增的抽象接口Book类的代码:

interface Book {
// 判断Book对象是否为空对象(Null Object)
public boolean isNull(); // 展示Book对象的信息内容。
public void show();
}

新增的空对象类NullBook类的代码(继承Book类):

public class NullBook implements Book {
public boolean isNull() {
return true;
} public void show() { }
}

原有的ConcreteBook类修改后的代码(增加对Book接口的实现,实现isNull方法):

public class ConcreteBook implements Book{
private int ID;
private String name;
private String author; // 构造函数
public ConcreteBook(int ID, String name, String author) {
this.ID = ID;
this.name = name;
this.author = author;
} /**
*
* Description About show: <br>
* 展示图书的相关信息
*
* @version V1.0
*/
public void show() {
System.out.println(ID + "**" + name + "**" + author);
}
public boolean isNull(){
return false;
}
}

工厂类(BookFactory)修改后的代码(返回对象从ConcreteBook改为Book,并当ID属于非法值或者不存在时,返回NullBook对象。):

public class BookFactory {
/**
* Description About getBook: <br>
* 根据ConcreteBook的ID,获取图书对象。
* @param ID 图书的ID
* @return 图书对象
* @version V1.0
*/ public Book getBook(int ID) {
Book book;//将原来的ConcreteBook改为Book
switch (ID) {
case 1:
book = new ConcreteBook(ID, "设计模式", "GoF");
break;
case 2:
book = new ConcreteBook(ID, "被遗忘的设计模式", "Null Object Pattern");
break;
default:
book = new NullBook();//创建一个NullBook对象
break;
} return book;
}
}

客户端的代码为:

public static void main(String[] args) {
BookFactory bookFactory = new BookFactory();
Book book = bookFactory.getBook(-1);
book.show();
}

运行一下,我们发现,即使传入的参数是非法值或者不存在的值时,也不会报错了,这是Null Object Pattern的第一个好处。但是现在不报错,也没有任何输出,肯定不够友好,不够人性化。此时,在NullBook类的show方法中,我们可以定制我们的输出提醒,当用户调用空对象的show方法时,就会输出我们定制的提醒。这回我们可以实现,一处定制,处处输出,主动权在我们手里,而不是在客户端的手里。这是Null Object Pattern的第二个好处。

比如我们进行如下修改,修改后的NullBook类代码:

public class NullBook implements Book {
public boolean isNull() {
return true;
} public void show() {
System.out.println("Sorry,未找到符合您输入的ID的图书信息,请确认您输入的不是非法值。");
}
}

此时,在执行一下Client,你会发现控制台输出为:Sorry,未找到符合您输入的ID的图书信息,请确认您输入的不是非法值。

其实,虽然在客户端我们不进行检测也可以保证程序不报错,但是最好的方式,还是进行相应的检测,如下:

  public static void main(String[] args) {
BookFactory bookFactory = new BookFactory();
Book book = bookFactory.getBook(-1);
if (book.isNull()) {
//这里由客户端定制提醒代码
System.out.println("兄弟,你输入的ID不符合规范吧。");
}else{
book.show();
}
}

我们看到相比之下,book.isNull()比book == null更加优雅一点。到这里,Null Object Pattern大概就介绍完了。我们可以看到,其实Null Object Pattern还是有点意思的,可以说使整个系统更加坚固了。

四、Consequences

Null Object Pattern,作为一种被遗忘的设计模式,却有着不能被遗忘的作用。

(1)它可以加强系统的稳固性,能有有效地防止空指针报错对整个系统的影响,使系统更加稳定。 
(2)它能够实现对空对象情况的定制化的控制,能够掌握处理空对象的主动权。 
(3)它并不依靠Client来保证整个系统的稳定运行。 
(4)它通过isNull对==null的替换,显得更加优雅,更加易懂。

五、总结

到这里,我们的Null Object Pattern就介绍完了,还可以参考这篇资料,也是讲得很不错的。http://www.cs.oberlin.edu/~jwalker/nullObjPattern/

本文转自:http://blog.csdn.net/qiumengchen12/article/details/44923139

被遗忘的设计模式——空对象模式(Null Object Pattern)的更多相关文章

  1. 用最简单的例子理解对象为Null模式&lpar;Null Object Pattern&rpar;

    所谓的"对象为Null模式",就是要求开发者考虑对象为Null的情况,并设计出在这种情况下的应对方法. 拿"用最简单的例子理解策略模式(Strategy Pattern) ...

  2. Java 空对象设计模式&lpar;Null Object Pattern&rpar; 讲解

    转自:http://www.cnblogs.com/haodawang/articles/5962531.html 有时候我们的代码中为避免 NullPointerException 会出现很多的对N ...

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

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

  4. Java设计模式之十三 ---- 观察者模式和空对象模式

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

  5. C&num; 设计模式之空对象模式

    最近看了不少的书籍和视频等相关资料,决定自己边学习边写一下个人对设计模式的理解,如果有不对的请大家多多指正. 今天先说说我个人觉得最简单的设计模式 -- [空对象模式] 空对象模式可以减少客户端对对象 ...

  6. 设计模式:空对象模式(Null Object Pattern)

    设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...

  7. 【设计模式 - 21】之空对象模式(Null Object)

    1      模式简介 在空对象模式中,一个空对象取代NULL对象的实例的检查.NULL对象不是检查空值,而是反映一个不做任何动作的关系.这样的NULL对象也可以在数据不可用的时候提供默认的行为. 在 ...

  8. 设计模式之美:Null Object(空对象)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Null Object 的示例实现. 意图 通过对缺失对象的封装,以提供默认无任何行为的对象替代品. Encapsulate t ...

  9. GoLang设计模式12 - 空对象模式

    空对象设计模式是一种行为型设计模式,主要用于应对空对象的检查.使用这种设计模式可以避免对空对象进行检查.也就是说,在这种模式下,使用空对象不会造成异常. 空对象模式的组件包括: Entity:接口,定 ...

随机推荐

  1. iOS的触摸事件的用法以及和手势识别器的区别

    1.首先来介绍下触摸事件和手势识别器的利与弊 触摸事件和手势识别器二者之间有直接的关系 手势识别器是在触摸事件的基础上演变过来的 当我们用到触摸事件时 默认的uiview是没有什么效果的 只能自定义v ...

  2. linux之SQL语句简明教程---BETWEEN

    IN 这个指令可以让我们依照一或数个不连续 (discrete) 的值的限制之内抓出数据库中的值,而BETWEEN 则是让我们可以运用一个范围 (range) 内抓出数据库中的值.BETWEEN 这个 ...

  3. java 多线程使用方法及Socket的使用

    public class newThread implements Runnable{ public void run(){ dosome(); } public void dosome(){ Sys ...

  4. 分布式版本控制系统Git-----1&period;Git 初识

    开始工作咯,师傅让我开始学习Git.刚接触我是懵逼的,"分布式版本控制系统"啥玩意啊这是,大家可不能从字面意思上理解啊,刚开始,版本控制么,我以为是团队合作的时候把开发工具.JDK ...

  5. 《Language Implementation Patterns》之 构建语法树

    如果要解释执行或转换一段语言,那么就无法在识别语法规则的同时达到目标,只有那些简单的,比如将wiki markup转换成html的功能,可以通过一遍解析来完成,这种应用叫做 syntax-direct ...

  6. &period;9-浅析express源码之请求处理流程&lpar;2&rpar;

    上节漏了几个地方没有讲. 1.process_params 2.trim_prefix 3.done 分别是动态路由,深层路由与最终回调. 这节就只讲这三个地方,案例还是express-generat ...

  7. C&num;中方法、类等的默认访问修饰符~

    C# 方法默认访问级别 : private C# 类默认访问级别 : internal 1.命名空间下的元素的默认访问修饰符 public : 同一程序集的其他任何代码或引用该程序集的其他程序集都可以 ...

  8. JS代码把JSON字符串转换为对象,计算对象的长度并把它转换为数字类型,把转换的值相加减

    Number(JSON.parse(rowObject.RenewalProperty).length)-1

  9. java框架篇---hibernate&lpar;多对多&rpar;映射关系

    以学生和老师为例的来讲解多对多映射. 实体类: Student package cn.itcast.g_hbm_manyToMany; import java.util.HashSet; import ...

  10. c&num; sql等微型代码工具LinqPad