美文网首页Java设计模式窥探设计模式之谜
设计模式--空对象模式(Null Object Pattern)

设计模式--空对象模式(Null Object Pattern)

作者: 蓦然飞跃 | 来源:发表于2018-08-10 16:27 被阅读2次

    一.问题的引入

    空对象模式到底是什么呢?在解开它的神秘面纱之前,我们先来看一个场景:

    李华刚工作了几年,靠努力积攒了一笔资金。这时候,他萌生了创业的想法。并经过团队的不懈努力,公司越做越大,数据库存储的职工也越来越多。李华,闲得蛋疼,没事想查询一下员工信息,于是发生了下面的故事。

    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());
            }
        }
    }
    

    三.总结

    Null Object Pattern,作为一种被遗忘的设计模式,却有着不能被遗忘的作用。

    (1)它可以加强系统的稳固性,能有有效地防止空指针报错对整个系统的影响,使系统更加稳定。
    (2)它能够实现对空对象情况的定制化的控制,能够掌握处理空对象的主动权。
    (3)它并不依靠Client来保证整个系统的稳定运行。
    (4)它通过isNull对==null的替换,显得更加优雅,更加易懂。

    说明:这里所讲的空对象模式(Null Object Pattern)是极为简单的内容,后期我将更为深入的讲解,敬请期待!希望能对你有所帮助。
    推荐阅读:
    设计模式--空对象模式(Null Object Pattern)-中阶
    设计模式--代理模式(Proxy Pattern)
    设计模式--代理模式(Proxy Pattern) 之 “高老庄悟空降八戒”

    相关文章

      网友评论

        本文标题:设计模式--空对象模式(Null Object Pattern)

        本文链接:https://www.haomeiwen.com/subject/aidkbftx.html