java1.8--Null Object模式
整理这篇博客是因为现在在整理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模式的更多相关文章
- 设计模式之空对象(Null Object)模式
通过引用Null Object,用于取消业务逻辑中对对象的为空推断 类图: Nullable: package com.demo.user; public interface Nullable { b ...
- Null Object模式
去除代码中的if(obj==null),或者try/catch语句.维持Code的一致性. Null对象,代表"什么也不做"的一个对象. 使Null对象称为一个匿名内部类确保了该类 ...
- 使用“Empty 模式”改进 Null Object
概述 Null Object 是Martin 大师提出的一种重构手段,其思想就是通过多态(派生一个Null对象)来减少逻辑(if … then …else)的判断. 而.NET中已经有Null Obj ...
- 设计模式之美:Null Object(空对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Null Object 的示例实现. 意图 通过对缺失对象的封装,以提供默认无任何行为的对象替代品. Encapsulate t ...
- 【设计模式 - 21】之空对象模式(Null Object)
1 模式简介 在空对象模式中,一个空对象取代NULL对象的实例的检查.NULL对象不是检查空值,而是反映一个不做任何动作的关系.这样的NULL对象也可以在数据不可用的时候提供默认的行为. 在 ...
- 设计模式:空对象模式(Null Object Pattern)
设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...
- 被遗忘的设计模式——空对象模式(Null Object Pattern)
GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...
- 【C#|.NET】从细节出发(三) 逻辑层事务和page object模式
一. 业务逻辑层的事务问题 如果你的程序分层清晰并且系统禁用复杂存储过程,那么在DA中的职责比较单一.程序的逻辑通过BLL调用各种不同模块的DA来实现数据操作.如果当需要不同模块在一个事务的时候,问题 ...
- 敏捷软件开发(3)---COMMAND 模式 & Active Object 模式
COMMAND 模式 command模式非常简单,简单到你无法想象的地方. public interface Command { void execute(); } 这就是一个command模式的样子 ...
随机推荐
- 编码(ACSII unicod UTF-8)、QT输出中文乱码深入分析
总结: 1. qt输出中文乱码原因分析 qt的编程环境默认是utf-8编码格式(关于编码见下文知识要点一): cout << "中文" << endl; 程 ...
- 微信小程序之实现slideUp和slideDown效果和点击空白隐藏
怎样实现jq中的slideUp或者slideDown这种动画效果呢,我的思路是用css3的transform: translateY()属性,给需要动画的元素添加上一个动画class. 先上效果图: ...
- java 读取数据库中文信息,为何在jsp页面中出现乱码
有如下几种解决办法:1.保证项目的字符编码和每一个jsp页面的字符编码一致,如果不一致可能导致中文乱码问题<%@ page language="java" contentTy ...
- visual core 运行 .net core bug处理
launch: program 'launch: launch.json must be configured. Change 'program' to the path to the executa ...
- POJ 3673 Cow Multiplication
Cow Multiplication Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 13312 Accepted: 93 ...
- POJ 3154 Graveyard【多解,数论,贪心】
Graveyard Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 1707 Accepted: 860 Specia ...
- UVa 10341 - Solve It【经典二分,单调性求解】
原题: Solve the equation: p*e-x + q*sin(x) + r*cos(x) + s*tan(x) + t*x2 + u = 0 where ...
- [51nod1197]字符串的数量 V2
用N个不同的字符(编号1 - N),组成一个字符串,有如下要求: (1) 对于编号为i的字符,如果2 * i > n,则该字符可以作为结尾字符.如果不作为结尾字符而是中间的字符,则该字符后面可以 ...
- 克鲁斯卡尔(Kruskal)算法求最小生成树
/* *Kruskal算法求MST */ #include <iostream> #include <cstdio> #include <cstring> #inc ...
- JS数组中shift()和push(),unshift()和pop()操作方法使用
Javascript为数组专门提供了push和pop()方法,以便实现类似栈的行为.来看下面的例子: var colors=new Array(); //创建一个数组 var count= ...