一.问题的引入
空对象模式到底是什么呢?在解开它的神秘面纱之前,我们先来看一个场景:
李华刚工作了几年,靠努力积攒了一笔资金。这时候,他萌生了创业的想法。并经过团队的不懈努力,公司越做越大,数据库存储的职工也越来越多。李华,闲得蛋疼,没事想查询一下员工信息,于是发生了下面的故事。
1.创建员工所对应的实体类
package com.yc.null_object_test;/** * Created by yucheng on 2018/8/10. */public class Person { private int id; private String name; private String adress; public Person(int id, String name, String adress) { this.id = id; this.name = name; this.adress = adress; } public int getId() { return id; } public String getName() { return name; } public String getAdress() { return adress; } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + ", adress='" + adress + '\'' + '}'; } }
2.创建PersonFactory,用来获取Person对象
package com.yc.null_object_test;/** * Created by yucheng on 2018/8/10. */public class PersonFactory { /** * * @param id:员工号 * @return:用来返回员工对象 */ public Person getPerson(int id){ Person p = null; switch (id){ case 3: p = new Person(3,"张三","上海"); break; case 4: p = new Person(4,"李四","北京"); break; case 5: p = new Person(5,"王五","青岛"); break; default: break; } return p; } }
3.创建客户端,用来查询员工信息
* Created by yucheng on 2018/8/10. */public class Cilent { public static void main(String[] args) { PersonFactory pf = new PersonFactory(); Person zs = pf.getPerson(3); Person ls = pf.getPerson(4); System.out.println("name = " + zs.getName()); System.out.println("lisi = " + ls.getName()); } }
4.输出结果
name = 张三 lisi = 李四
结果一切看起来很美好,但是公司具体有多少人他自己也不是很清楚,于是乎,李华进行了下面的查询:
public class Cilent { public static void main(String[] args) { PersonFactory pf = new PersonFactory(); Person x = pf.getPerson(100); System.out.println("x = " + x.getName()); } }
Exception in thread "main" java.lang.NullPointerException at com.yc.null_object_test.Cilent.main(Cilent.java:10)
我嘞个去,发生了什么,李华一脸懵逼!!!
没错,是的。这就是我们在编程过程中,经常会遇见的一个问题,空指针异常。原因在于,我们传入了非法id=100。在PersonFactory中找不到对应的case值,从而导致对象x为null。而当我们使用空对象的引用时,就会报空指针异常。因此我们需要在操作对象之前对其进行判断如下:
public class Cilent { public static void main(String[] args) { PersonFactory pf = new PersonFactory(); Person x = pf.getPerson(100); if (x == null){ System.out.println("对象为空!x = " + x); } else { System.out.println("x = " + x.getName()); } } }
但是,你有没有想过,当我们对象的操作比较少时,这种判断的方法是很简洁有效,但当我们需要进行一系列操作时,又会增加多大的工作量呢?那么,有没有一劳永逸的做法呢
是的,正如你所想,空对象模式能够很好地解决我们的问题。
二.空对象模式
Problem
1.在编程的过程中,每次使用引用时,测试其是否为空总是我们避不开的一个话题,及其枯燥,而且势必将产生相当乏味的代码。
2.一旦我们忘记判断,NullPointerException就会映入眼帘,而你就得老老实实地的去慢慢检查。真的很闹心,反正我是这样认为的。
Solutions
空对象
它可以接收传递给它的所代表对象的信息,但是将返回表示为实际上并不存在任何“真实”的对象的值。通过这种方式,你可以假设所有的对象都是有效的,而不必浪费巨大精力去检查null。
三.解决问题
我们仍然围绕上面的那个问题来讨论,寻求解决方案:
1.空对象模式(Null Object Pattern)的类结构图
57.png
2.代码实现
在上面程序的基础上进行修改
<1> 创建抽象接口
package com.yc.null_object3;/** * Created by yucheng on 2018/8/10. */public interface AbstractPerson { String getName();// 用来获取对象的名字 boolean isNull();// 判断对象是否为空}
<2> 创建空对象类
package com.yc.null_object3;/** * Created by yucheng on 2018/8/10. */public class NullPerson implements AbstractPerson { @Override public String getName() { return "NullPerson{对象为空!}"; } @Override public boolean isNull() { return true; } }
<3> 创建实际对象类
package com.yc.null_object3;/** * Created by yucheng on 2018/8/10. */public class Person implements AbstractPerson { private int id; private String name; private String adress; public Person(int id, String name, String adress) { this.id = id; this.name = name; this.adress = adress; } public String getName() { return name; } @Override public boolean isNull() { return false; } }
<4> 创建对象工厂
package com.yc.null_object3;/** * Created by yucheng on 2018/8/10. */public class PersonFactory { public AbstractPerson getPerson(int id) { AbstractPerson p = null; switch (id){ case 3: p = new Person(3,"张三","上海"); break; case 4: p = new Person(4,"李四","北京"); break; case 5: p = new Person(5,"王五","上海"); break; default: p = new NullPerson();//当有不合法输入时,用空对象来填充 break; } return p; } }
<5> 创建客户端类
package com.yc.null_object3;/** * Created by yucheng on 2018/8/10. */public class Cilent { public static void main(String[] args) { // 合法输入 PersonFactory pf = new PersonFactory(); AbstractPerson p1 = pf.getPerson(3); AbstractPerson p2 = pf.getPerson(4); // 非法输入 AbstractPerson p3 = pf.getPerson(100); System.out.println("p1 = " + p1.getName()); System.out.println("p2 = " + p2.getName()); System.out.println("p3 = " + p3.getName()); } }
<6> 结果展示
p1 = 张三 p2 = 李四 p3 = NullPerson{对象为空!}
由输出结果,我们可以知晓,即使我们进行了非法输入,也不会报错了,这是空对象模式的第一个好处。
另外在NullBook类的show方法中,我们可以定制我们的输出提醒,当用户调用空对象的show方法时,就会输出我们定制的提醒。这回我们可以实现,一处定制,处处输出,主动权在我们手里,而不是在客户端的手里。这是Null Object Pattern的第二个好处。
其实呢,我们在客户端不进行判断,程序也不会报错,但是最佳的方式,还是进行判断。
package com.yc.null_object3;/** * Created by yucheng on 2018/8/10. */public class Cilent { public static void main(String[] args) { // 合法输入 PersonFactory pf = new PersonFactory(); AbstractPerson p3 = pf.getPerson(100); if(p3.isNull()){ System.out.println("兄弟!你进行了非法id访问!"); } else{ System.out.println("p3 = " + p3.getName()); } } }
作者:蓦然飞跃
链接:https://www.jianshu.com/p/3b8da46616e8