整理这篇博客是因为现在在整理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. 关于CSS的table-layout属性的用法详解

    前言: 今天来和大家详细说一下table-layout属性的用法. /*eg:设置表格布局算法*/ table{ table-layout:fixed; } ***本文关键词:table-layout ...

  2. python的流程控制学习

    _username= 'haozheyu' _password= ' username = input("请输入用户:") password = input("请输入密码 ...

  3. [PHP] 编译构建最新版PHP源码

    获取最新PHP代码git clone https://git.php.net/repository/php-src.git构建编译环境apt-get install build-essential 编 ...

  4. Ubuntu下配置ShadowS + Chrome

    // 这是一篇导入进来的旧博客,可能有时效性问题. 题目和全文中的ShadowS指代以ShadowS开头名字的某工具,以预防文章被和谐.本机Ubuntu 14.04 LTS.在apt-get upda ...

  5. RED_HAWK:基于PHP实现的信息收集与SQL注入漏洞扫描工具

    无事早上就去逛freebuf看到一款不错的工具,打算介绍给大家 RED_HAWK:基于PHP实现的信息收集与SQL注入漏洞扫描工具 RED HAWK 最新版本:v1.0.0[2017年6月11日] 下 ...

  6. JavaScript中的this(你不知道的JavaScript)

    JavaScript中的this,刚接触JavaScript时大家都在大肆渲染说其多么多么的灵巧重要,然而自己并不关心:随着自己对JavaScript一步步深入了解,突然恍然大悟,原来它真的很重要!所 ...

  7. python实现求最长子串长度

    给定一个字符串,求它最长的回文子串长度,例如输入字符串'35534321',它的最长回文子串是'3553',所以返回4. 最容易想到的办法是枚举出所有的子串,然后一一判断是否为回文串,返回最长的回文子 ...

  8. HashMap 源码详细分析(JDK1.8)

    一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...

  9. bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

    Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...

  10. [51nod1709]复杂度分析

    给出一棵n个点的树(以1号点为根),定义dep[i]为点i到根路径上点的个数.众所周知,树上最近公共祖先问题可以用倍增算法解决.现在我们需要算出这个算法精确的复杂度.我们定义计算点i和点j最近公共组先 ...