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


       前面我们已经详细介绍了空对象模式的概念、由来、实现以及利弊。并且我们也已经知道了使用空对象的目的主要在于解决下面两个问题:
1.去掉那该死的null检查
2.避免程序抛出NullPointerException异常
  但是我们这里得提到一个说法:“被遗忘的空对象模式”。为什么说被遗忘呢,我个人的理解是:应用少而被遗忘。反正我在编程的过程中,对此模式的应用也是少之又少。因此,我认为知道它是怎么回事就可以了。
  但是最近在看《Thinking in Java 4th》的时候发现,原来空对象还可以这么用。这不,忍不住,想和大家一起分享探讨一下。


问题描述

       当黑子的公司规模发展到一定程度时,他的任务不再是那么轻松了,整天没事尽搞些瞎几把查询。它得为应对公司发展的需要设置许多新的职位,并且为这些职位招聘天下英才。

实现

1.创建标记接口
       我们知道,正常情况下,我们使用一个对象的引用时,第一步是要对对象进行是否为空的判断,才能防止异常的发生。而我们知道,学过“空对象模式”后,我们就可以使用空对象来解决问题。
  但是,在初阶中我们也提到了,即使你使用了“空对象模式”,最好的策略是仍然进行null的判断,不过此时,我们的方法是,创建一抽象接口,在接口内定义isNull()方法,进行判断。
但最简单的方式是创建一个标记接口

package com.yc.null_object;

/**
 * Created by yucheng on 2018/8/9.
 * 说明:创建一个标记接口,对对象进行是否为空的最简单判断方式
 *       使得我们可以使用instanceof探测空对象,毕竟isNull()和
 *       instanceof只是RTTI(运行时类型识别)的不同方式,为什么
 *       不用内置的呢
 */
public interface Null {
}

2.创建“含空对象实体类”的非空的实体类
说明:表示空对象类,此处是放在Person的内部作为一个静态内部类的存在(当然你将其独立出来也没毛病),个人觉得,结构虽然变得复杂了一些,但是整体性更好。不得不佩服大神的实力!
而且空对象,通常是单例的(因为我们只需要一个呀),因此我们可以将其作为static final(编译期常量)来创建,可以减少运行时的压力嘛!

package com.yc.null_object;
/**
 * Created by yucheng on 2018/8/9.
 */
public class Person {
    public final String first;
    public final String last;
    public final String adress;

    public Person(String first, String last, String adress) {
        this.first = first;
        this.last = last;
        this.adress = adress;
    }
    @Override
    public String toString() {
        return "Person{" +
                "first='" + first + '\'' +
                ", last='" + last + '\'' +
                ", adress='" + adress + '\'' +
                '}';
    }
    // 创建一个静态内部类,用来创建空对象
    public static class NullPerson extends Person implements Null{
        public NullPerson() {
            super("None", "None", "None");
        }
        @Override
        public String toString() {
            return "NullPerson";
        }
    }
    // 创建一个编译期常量,并将其初始化为空对象
    public static final Person NULL = new NullPerson();
}

3.创建职位实体(Position)
       现在到了黑子招兵买马,大展身手的时候了。但是在虚位以待时,我们就可以将Person空对象放在每一个Position上。即我们为每个职位预留一个名额,没有人时,我们在上面放空对象,当招聘到了人以后,我们就将新员工填充到到该职位上。

package com.yc.null_object;
/**
 * Created by yucheng on 2018/8/9.
 * Position表示公司的职位,但此时也许有的职位还没有人,因此我们可以先将
 * 这个职位留出来,只不过,用空对象对它进行填充,当以后需要的时候,再对其进行填充
 */
public class Position {
    private String title;
    private Person person;

    // 创建有人任职的职位
    public Position(String jobTitle, Person Employee) {
        this.title = jobTitle;
        this.person = Employee;
        if (person == null){
            // 如果对象为空,就将其转为NullPerson空对象
            person = Person.NULL;
        }
    }
    // 创建没有人任职的职位
    public Position(String jobTitle) {
        this.title = jobTitle;
        // 当构造器中没有传入Person对象,默认将其设置为NullPerson空对象
        person = Person.NULL;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String newTitle) {
        this.title = newTitle;
    }
    public Person getPerson() {
        return person;
    }
    public void setPerson(Person newPerson) {
        this.person = newPerson;
        if (person == null){
            person = Person.NULL;
        }
    }
    @Override
    public String toString() {
        return "Position{" +
                "title='" + title + '\'' +
                ", person=" + person +
                '}';
    }
}

4.创建Staff类,进行Position对象的创建、存储和查询
这是一个非常有创造力的类,简洁且功能强大!
废话先不多说,直接上程序!

package com.yc.null_object;
import java.util.ArrayList;
/**
 * Created by yucheng on 2018/8/10.
 */
public class Staff extends ArrayList<Position>{
    // 我们可以将Staff重载的add方法,显示的写出来,如下:
    // 发现,它是调用了父类的add方法,将Position对象存储在对象中,
    // 因为,Staff继承自ArrayList,因此它本身也是一个List
    @Override
    public boolean add(Position position) {
        return super.add(position);
    }
    // 自行对add方法进行重载
    public void add(String title,Person person){
        // 此处add()方法是继承自ArrayList
        add(new Position(title,person));
    }
    // 自行对add方法进行重载
    public void add(String... titles) {
        for (String title : titles) {
            // 创建空职位,此处调用的是默认的从父类继承来的add方法
            add(new Position(title));
        }
    }
    // 构造器
    public Staff(String... titles){
        add(titles);
    }
    // 判断职位是否可以填充
    public boolean positionAvailable(String title){
        // 此处的this表示的是调用此方法的Staff对象
        for (Position position:this) {
            if (position.getTitle().equals(title)&&position.getPerson() == Person.NULL){
                return true;
            }
        }
        return false;
    }
    // 填充职位
    public void fillPosition(String title,Person hire){
        for (Position position:this){
            if (position.getTitle().equals(title)&&position.getPerson() == Person.NULL){
                position.setPerson(hire);
                return;
            }
        }
        throw new RuntimeException("Position " + title + "not available");
    }

    // 测试 
    public static void main(String[] args) {
        Staff staff = new Staff("President","CTO",
                "Marketing Manager","Product Manager",
                "Project Lead","Software Engineer",
                "Software Engineer","Software Engineer",
                "Software Engineer","Test Engineer","Technical Writer");
        staff.fillPosition("Project Lead",new Person("Janet","Planner","The Top ,Lonely At"));
        staff.fillPosition("President",new Person("Me","Last","The Burbs"));
        if (staff.positionAvailable("Software Engineer")){
            staff.fillPosition("Software Engineer",new Person("Bob","Coder","Bright Light City"));
        }
        System.out.println(staff);
    }
}

为什么说这个class厉害呢,下面我们来分析一下:
1.继承自ArrayList,使得对象具有List的存储功能
public class Staff extends ArrayList<Position>
这样一来,我们的Staff对象就能够想List一样对Position对象进行存储、查询等操作,如add()。
2.不定参数的方式,使得我们一次可以创建多个Position对象
public void add(String... titles) {}
这样,我们就可以一次传入多个职位名称titles,然后循环调用构造器,就行对象的创建,而不必一个个的创建了。
3.方法的重载

    // 第一个add方法
    @Override
    public boolean add(Position position) {
        return super.add(position);
    }
    // 第二个add方法
    public void add(String title,Person person){
        // 此处add()方法是继承自ArrayList
        add(new Position(title,person));
    }
    // 第三个add方法
    public void add(String... titles) {
        for (String title : titles) {
            // 创建空职位
            add(new Position(title));
        }
    }

说明:
第一个add方法:是继承自父类ArrayList,是对Position对象进行操作
第二个add方法:是我们自己定义的add重载方法,目的在于将非空Position对象存储到Staff中。
第二个add方法:是我们自己定义的add重载方法,目的在于将空Position对象存储到Staff中。Staff此时就是一个ArrayList。

总结

山重水复疑无路,柳暗花明又一村!只有你想不到,没有做不到!长知识了!

提示:下一节,我将继续“空对象模式”的最后一节,用动态代理创建空对象

推荐阅读:
设计模式--空对象模式(Null Object Pattern)-初阶
设计模式--代理模式(Proxy Pattern)
设计模式--代理模式(Proxy Pattern) 之 “高老庄悟空降八戒”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容