整理这篇博客是因为现在在整理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. ppt的那些小事(一)

    根据应用场景不同,幻灯片可以分为两大类,演讲型和阅读型 模板资源:(免费) OfficePLUS,微软Office官方在线模板网站!http://www.officeplus.cn/Template/ ...

  2. 关于S/4HANA里Sales Office 和Sales Organization那些事儿

    今天这篇文章来自我的成都同事Zhang Sean(张正永). Sean也是一位在SAP行业摸爬滚打多年的老兵了,2009年从大学硕士毕业之后就进入了SAP Labs从事开发工作,目前是SAP 成都S/ ...

  3. 2017 年的 人生 hard 模式终于结束了,2018年回归初心(二)

    今天周末, 深圳的天气简直好的不像话.好了,我们继续之前的话题往下聊. >>>猎头 : 关于猎头这个行业,以笔者的感觉来说 一般你工作年限未超过三年的话,你是很难遇到猎头来推送你的简 ...

  4. QA: 自闭合标签要不要手动闭合?

    起 自闭合标签末尾要不要加 /,这个问题一直 "困扰" 着我.但是抱着无所谓的态度,一直没有仔细去看下. 以 img 标签为例,一般有以下三种写法: <img src=&qu ...

  5. 浅谈Android的广告欢迎界面(倒计时)

    前些时候就是别人问我他的android APP怎么做一个广告的欢迎界面,就是过几秒后自动跳转到主界面的实现. 也就是下面这种类似的效果.要插什么广告的话你就换张图吧. 那么我就思考了下,就用了andr ...

  6. C#并发编程实例讲解-概述(01)

    在工作中经常遇到需要并发编程的实例,一直没有时间来整理,现在空了下来,个人整理对并发一下理解. 关于并发编程的几个误解 误解一:并发编程就是多线程 实际上多线只是并发编程的一中形式,在C#中还有很多更 ...

  7. 终于理解kalman滤波

    2017拜拜啦,怎么过元旦呢?当然是果断呆实验室过... 应该是大二的时候首次听说kalman,一直到今天早上,我一看到其5条"黄金公式",就会找各种理由放弃,看不懂呀...但是研 ...

  8. deeplearning.ai 卷积神经网络 Week 3 目标检测 听课笔记

    本周的主题是对象检测(object detection):不但需要检测出物体(image classification),还要能定位出在图片的具体位置(classification with loca ...

  9. 基于Xilinx FPGA的视频图像采集系统

    本篇要分享的是基于Xilinx FPGA的视频图像采集系统,使用摄像头采集图像数据,并没有用到SDRAM/DDR.这个工程使用的是OV7670 30w像素摄像头,用双口RAM做存储,显示窗口为320x ...

  10. 2017西安网络赛 F

    f(cos(x))=cos(n∗x) holds for all xx. Given two integers nn and mm, you need to calculate the coeffic ...