整理这篇博客是因为现在在整理java8中的optional,所以觉得很有必要整理下Null Object模式。java.lang.NullPointerException,只要敢自称Java程序员,那对这个异常就再熟悉不过了。为了防止抛出这个异常,我们经常会写出这样的代码:

Person person = people.find("LinkinPark...");
if (person != null)
{
person.doSomething();
}

遗憾的是,在绝大多数Java代码里,我们常常忘记了判断空引用,所以,NullPointerException便也随之而来了。“Null Sucks.”,这就是Doug Lea对空的评价。作为一个Java程序员,如果你还不知道Doug Lea是谁,那赶紧补课,没有他的贡献,我们还只能用着Java最原始的装备处理多线程。"I call it my billion-dollar mistake.",有资格说这话是空引用的发明者,Sir C. A. R. Hoare。你可以不知道Doug Lea,但你一定要知道这位老人家,否则,你便没资格使用快速排序。



OK,现在我们就开始整理下Null Object模式。

在整理Null Object模式之前,我们先来了解下多态的另外一个好处。多态的最根本好处在于,你不必再向对象询问“你是什么类型”而后根据得到的答案调用对象的某个行为,你只管调用该行为就是了,其他的一切多态机制会为你安排妥当。当某个字段内容是null时,多态可扮演另一个较不直观,也较不为人所知的用途。就是设定一种可能某种状态是null的对象,然后在原来对象有可能出现null状态判断的时候不用直接在这个对象上控制,而是用现在这个null
object来取代原来的对象。具体的我们来举一个例子来研究下:

下面是原始代码:


package com.linkin.maven.mavenTest;

public class Test
{
private String customerName;//模拟一个顾客名字 //下面方法模拟业务逻辑处理代码
public void test(Site site)
{
if (site.getCustomer() == null)
{
customerName = "这里是可能出现null值的情况";
}
else
{
customerName = site.getCustomer().getName();
}
System.out.println(customerName);
} public String getCustomerName()
{
return customerName;
} public static void main(String[] args)
{
Test test = new Test();
Site site = new Site();
//先模拟可能出现null值的情况
test.test(site);
//模拟正常的情况
site.setCustomer(new Customer());
test.test(site);
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个场所类,里面有一个顾客属性表示顾客。
*/
class Site
{
private Customer customer; public Customer getCustomer()
{
return customer;
} public void setCustomer(Customer customer)
{
this.customer = customer;
}
} /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个顾客类
*/
class Customer
{
public String getName()
{
return "这里是正常情况的返回值"; } }

上面的代码我们看到了,里面有一个null值判断,如果是null处理null值情况,如果非null那么处理正常逻辑。现在我们用Null Object模式来将上面出现的null的情况的逻辑代码抽离到null object对象里面。现在我们在原来的基础上修改,代码如下:

package com.linkin.maven.mavenTest;

public class Test
{
private String customerName;//模拟一个顾客名字 //下面方法模拟业务逻辑处理代码
public void test(Site site)
{
Customer customer = site.getCustomer();
if (customer.isNull())
{
customerName = "这里是可能出现null值的情况";
}
else
{
customerName = customer.getName();
}
System.out.println(customerName);
} public String getCustomerName()
{
return customerName;
} public static void main(String[] args)
{
Test test = new Test();
Site site = new Site();
//先模拟可能出现null值的情况
test.test(site);
//模拟正常的情况
site.setCustomer(new Customer());
test.test(site);
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个场所类,里面有一个顾客属性表示顾客。
*/
class Site
{
private Customer customer; public Customer getCustomer()
{
return customer == null ? Customer.newNull() : customer;
} public void setCustomer(Customer customer)
{
this.customer = customer;
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个顾客类
*/
class Customer implements Nullable
{
public String getName()
{
return "这里是正常情况的返回值";
} //个人建议创建一个工厂函数,专门用来创建NullCustomer()对象,这样子外部就不知道这个空对象的存在了。
static Customer newNull()
{
return new NullCustomer();
} @Override
public boolean isNull()
{
return false;
} } class NullCustomer extends Customer implements Nullable
{ @Override
public boolean isNull()
{
return true;
} } interface Nullable
{
boolean isNull();
}

OK,现在我们重新定义了一个NullCustomer的对象用来做Null Object对象,但是我们的控制分支还在,现在我们继续重构,将上面的null值情况的业务处理代码搬入到null Object对象中,就可以去掉null分支判断了,我们在上面的基础上继续重构,代码如下:

package com.linkin.maven.mavenTest;

public class Test
{
private String customerName;//模拟一个顾客名字 //下面方法模拟业务逻辑处理代码
public void test(Site site)
{
Customer customer = site.getCustomer();
//将原来的控制分支利用java多态,分别移入到了2个对象中,这里就不存在分支了
customerName = customer.getName();
System.out.println(customerName);
} public String getCustomerName()
{
return customerName;
} public static void main(String[] args)
{
Test test = new Test();
Site site = new Site();
//先模拟可能出现null值的情况
test.test(site);
//模拟正常的情况
site.setCustomer(new Customer());
test.test(site);
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个场所类,里面有一个顾客属性表示顾客。
*/
class Site
{
private Customer customer; public Customer getCustomer()
{
return customer == null ? Customer.newNull() : customer;
} public void setCustomer(Customer customer)
{
this.customer = customer;
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个顾客类
*/
class Customer implements Nullable
{ @Override
public boolean isNull()
{
return false;
} //正常分支不变,还是处理原来的逻辑
public String getName()
{
return "这里是正常情况的返回值";
} //个人建议创建一个工厂函数,专门用来创建NullCustomer()对象,这样子外部就不知道这个空对象的存在了。
static Customer newNull()
{
return new NullCustomer();
} } class NullCustomer extends Customer implements Nullable
{ @Override
public boolean isNull()
{
return true;
} //原始业务代码逻辑中的控制分支中,出现Null值情况的分支移入到Null Object对象中了
public String getName()
{
return "这里是可能出现null值的情况";
} } interface Nullable
{
boolean isNull();
}

OK了,现在代码写完了,完美的去掉了null值判断,关于null值可能出现的情况的业务处理代码我们都放到了Null Object里面了去。



关于Null Object记住一点,空对象一定是常量,它们的任何成分都不会发生变化。因此我们应该使用单例模式,这样子就不管什么时候,我们获得的Null Object对象都是同一个唯一实例了。

下面的代码是最终代码:

package com.linkin.maven.mavenTest;

public class Test
{
private String customerName;//模拟一个顾客名字 //下面方法模拟业务逻辑处理代码
public void test(Site site)
{
Customer customer = site.getCustomer();
//将原来的控制分支利用java多态,分别移入到了2个对象中,这里就不存在分支了
customerName = customer.getName();
System.out.println(customerName);
} public String getCustomerName()
{
return customerName;
} public static void main(String[] args)
{
Test test = new Test();
Site site = new Site();
//先模拟可能出现null值的情况
test.test(site);
//模拟正常的情况
site.setCustomer(new Customer());
test.test(site);
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个场所类,里面有一个顾客属性表示顾客。
*/
class Site
{
private Customer customer; public Customer getCustomer()
{
return customer == null ? Customer.newNull() : customer;
} public void setCustomer(Customer customer)
{
this.customer = customer;
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: 模拟一个顾客类
*/
class Customer implements Nullable
{ @Override
public boolean isNull()
{
return false;
} //正常分支不变,还是处理原来的逻辑
public String getName()
{
return "这里是正常情况的返回值";
} //个人建议创建一个工厂函数,专门用来创建NullCustomer()对象,这样子外部就不知道这个空对象的存在了。
static Customer newNull()
{
return NullCustomer.getInstance();
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: Null Object对象,使用单例模式,继承正常业务对象,实现null值判断接口
*/
class NullCustomer extends Customer implements Nullable
{
private static NullCustomer nullCustomer; private NullCustomer()
{ } public static NullCustomer getInstance()
{
if (nullCustomer == null)
{
return new NullCustomer();
}
else
{
return nullCustomer;
}
} @Override
public boolean isNull()
{
return true;
} //原始业务代码逻辑中的控制分支中,出现Null值情况的分支移入到Null Object对象中了
public String getName()
{
return "这里是可能出现null值的情况";
} } /**
* @创建作者: LinkinPark
* @创建时间: 2015年10月30日
* @功能描述: Null值判断接口,正常业务对象和Null值对象都应该实现的接口
*/
interface Nullable
{
boolean isNull();
}

关于上面的重构,以后我会有专门的博客来整理重构的,这里说的就是要一遍重构,一遍测试。上面4个代码的测试结果都一样,也就是表示重构的没问题。下面我贴出上面的控制台输出:





总结:

在Java世界里,解决空引用问题常见的一种办法是,使用Null Object模式。这样的话,在“没有什么”的情况下,就返回Null Object,客户端代码就不用判断是否为空了。但是,这种做法也有一些问题。首先,我们肯定要为Null Object编写代码,而且,如果我们想大规模应用这个模式,我们要为几乎每个类编写Null Object。然后,有的时候业务处理稍微复杂一点,比如一个类包含了多个业务对象属性,这个时候的Null Object对象就会有好几个,编码量也大大的增加了。我们有必要了解这种合理的绕开null值判断的情况,但是如果实际编码这样子写Null
Object类的话,也是一件很恶心的事情。java1.8中引入了Optional类,使用这个类我们可以很方便的实现上面的功能而且不用写很多代码的,这也就是我下篇博客要整理的。



java1.8--Null Object模式的更多相关文章

  1. 设计模式之空对象(Null Object)模式

    通过引用Null Object,用于取消业务逻辑中对对象的为空推断 类图: Nullable: package com.demo.user; public interface Nullable { b ...

  2. Null Object模式

    去除代码中的if(obj==null),或者try/catch语句.维持Code的一致性. Null对象,代表"什么也不做"的一个对象. 使Null对象称为一个匿名内部类确保了该类 ...

  3. 使用“Empty 模式”改进 Null Object

    概述 Null Object 是Martin 大师提出的一种重构手段,其思想就是通过多态(派生一个Null对象)来减少逻辑(if … then …else)的判断. 而.NET中已经有Null Obj ...

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

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

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

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

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

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

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

    GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...

  8. 【C#|.NET】从细节出发(三) 逻辑层事务和page object模式

    一. 业务逻辑层的事务问题 如果你的程序分层清晰并且系统禁用复杂存储过程,那么在DA中的职责比较单一.程序的逻辑通过BLL调用各种不同模块的DA来实现数据操作.如果当需要不同模块在一个事务的时候,问题 ...

  9. 敏捷软件开发(3)---COMMAND 模式 & Active Object 模式

    COMMAND 模式 command模式非常简单,简单到你无法想象的地方. public interface Command { void execute(); } 这就是一个command模式的样子 ...

随机推荐

  1. 深入理解ES6之—符号与符号属性

    在js已有的基本类型(字符串,数值,布尔型,null和undefined)之外,es6引入了一种新的基本类型:==符号(Symbol)==.符号起初被设计用于创建对象私有成员. 符号没有字面量形式,你 ...

  2. PE文件详解(三)

    本文转自小甲鱼的PE文件详解系列传送门 PE文件到内存的映射 在执行一个PE文件的时候,windows 并不在一开始就将整个文件读入内存的,二十采用与内存映射文件类似的机制. 也就是说,windows ...

  3. For循环将将数字集合分类写入字典

    有以下数字集合[11,22,33,44,55,66,77,88,99,25,35,45,66,88],将所有大于66的值保存至字典的第一个key中,将小于66的值保存至第二个key的值中.即{'k1' ...

  4. better-scroll不能滚动之 滚动监听-左右联动

    滚动监听 better-scroll 无法滚动的分析,直接翻到最后,看问题汇总,希望能帮助你解决. 借用一下人家这个好看的项目图片,做一个解释.左边的内容会跟右边的内容一起关联,点击左边的菜单,右边会 ...

  5. Nginx是如何处理Request的?

    nginx是如何匹配过来的请求,然后做处理的呢?这个匹配的过程可以分为两步: 1.选择server 2.选择location    选择server 仅仅匹配server name 加入Nginx的配 ...

  6. 一个vue项目的简单分享

    回首用vue已经2个多月了,今年7月底根据vue社区提供的api写了一个小移动端的小dom 通过这个项目也让我更深入的了解了vue(组件之间的通讯,计算属性,数据绑定.数据驱动....),用数据驱动型 ...

  7. CTF---隐写术入门第二题 小苹果

    小苹果分值:10 来源: hanyuhang 难度:易 参与人数:2159人 Get Flag:862人 答题人数:996人 解题通过率:87% flag格式: CTF{} 解题链接: http:// ...

  8. bzoj:1723: [Usaco2009 Feb]The Leprechaun 寻宝

    Description 你难以想象贝茜看到一只妖精在牧场出现时是多么的惊讶.她不是傻瓜,立即猛扑过去,用她那灵活的牛蹄抓住了那只妖精.     “你可以许一个愿望,傻大个儿!”妖精说.     “财富 ...

  9. POJ 3154 Graveyard【多解,数论,贪心】

    Graveyard Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1707   Accepted: 860   Specia ...

  10. [bzoj3910] 火车

    一开始看错题了... 求经过的道路数量就求个lca,把路径上的点置为已经过的话,可以写一个并查集,把某个点往上连续已经过的一段点并起来. #include<cstdio> #include ...