设计模式---空对象模式

空对象模式

1、定义

在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。

英文定义如下:

Provide an object as a surrogate for the lack of an object of a given type. The Null Object provides intelligent do nothing behavior, hiding the details from its collaborators.

意思是:为缺少的对象提供一个默认的无意义对象,用来避免 Null 对象的产生。

简单来说,就是用一个空对象,来取代程序中的 Null 值判断,从而让调用者可以直接使用对象,而无需关心对象是否为 Null。

例如,在没用空对象模式之前,要正确的获取以下值:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase(); 

它的实现代码如下:

if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            if (isocode != null) {
                isocode = isocode.toUpperCase();
            }
        }
    }
}

非空判断已经多到令我们崩溃了,如果属性中还有更多的对象,那 Null 值判断就更多了,为了解决这个问题,就要使用本文将要介绍的空对象模式了。

2、组成角色

空对象模式包含如下角色:

  • 抽象对象(Abstract Object)角色:声明统一的对象行为(属性和方法);
  • 具体对象(Concrete Object)角色:确实存在的具体对象,程序中的非 Null 对象;
  • 空对象(Null Object)角色:非具体存在的对象,Null 对象;
  • 对象工厂(Object Factory)角色:根据传递的标识得到相关类的工厂类,返回值可以是具体对象或 Null 对象。

角色之间的 UML 关系图如下: 

 

3、空对象模式代码实现

3.1 抽象对象

/**
 * 抽象对象
 */
abstract class AbstractObject {
    String name;
    abstract String getName();
    abstract boolean isNull();
}

3.2 具体对象

/**
 * 具体对象
 */
class ConcreteObject extends AbstractObject {
    public ConcreteObject(final String name) {
        this.name = name;
    }
    @Override
    String getName() {
        return this.name;
    }
    @Override
    boolean isNull() {
        return false;
    }
}

3.3 空对象

/**
 * 空对象
 */
class NullObject extends AbstractObject {
    @Override
    String getName() {
        return "Not Available in Customer Database";
    }
    @Override
    boolean isNull() {
        return true;
    }
}

3.4 对象工厂

/**
 * 对象生成工厂
 */
class ObjectFactory {
    public static AbstractObject creator(final String name) {
        AbstractObject result = null;
        switch (name) {
            case "Java":
                result = new ConcreteObject("Java");
                break;
            case "SQL":
                result = new ConcreteObject("SQL");
                break;
            default:
                result = new NullObject();
                break;
        }
        return result;
    }
}

程序执行结果如下:

Java 

Not Available in Customer 

Database SQL

从以上的代码可以看出,其中 getName () 为所有对象需要执行的公共方法,如果没使用空对象模式的情况下,每次在调用 getName () 之前,我们需要先判空再使用,而如果使用的是空对象模式的话,则可以直接使用(该方法)。

4、优缺点

空对象模式的优点:

  • 省去代码中对 Null 值的判断和检查;
  • 让代码显的更加优雅和可读性更高;
  • 让系统更加稳定,避免程序抛出 NullPointerException 异常。

空对象模式的缺点:

  • 因为增加了更多的类信息,从而使系统更复杂。

5、应用场景

JDK 8 中的 Optional 对象使用的就是空对象模式,避免空指针的异常,同时又能写出优雅而简洁的 Java 代码。

Optional 类中有以下几个重要的方法:

  • ofNullable () 方法:为指定的值创建一个 Optional, 如果指定的值为 null,则返回一个空的 Optional 对象;
  • orElse () 方法:如果有值则将其返回,否则返回指定的其它值;
  • map () 方法:如果创建的 Optional 中的值存在,对该值执行提供的 Function 函数调用;
  • flagMap () 方法:如果创建的 Optional 中的值存在,就对该值执行提供的 Function 函数调用,返回一个 Optional 类型的值,否则就返回一个空的 Optional 对象。

小贴士:很多人可能对 “对该值执行提供的 Function 函数调用” 这句话不太理解,它的意思是说,例如下面代码: 

Optional.ofNullable(concreteUser).flatMap(u -> u.getAddress()) 
//其中 “(u -> u.getAddress ())” 这部分代码就是 “该值执行提供的 Function 函数”。

接下来我们就是用 Optional 对象,优雅的实现判空操作,优雅的实现文章开头 4 层令人崩溃的 Null 值判断,实现代码如下。

5.1 用户类

/**
 * 用户类
 **/
class User {
    public User(Address address) {
        this.address = address;
    }
    private Address address;
    public Optional<Address> getAddress() {
        return Optional.ofNullable(address);
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

5.2 地址类

/**
 * 地址类
 **/
class Address {
    public Address(Country country) {
        this.country = country;
    }
    private Country country;
    public Optional<Country> getCountry() {
        return Optional.ofNullable(country);
    }
    public void setCountry(Country country) {
        this.country = country;
    }
}

5.3 国际编码类

/**
 * 国际编码类
 **/
class Country {
    public Country(String isoCode) {
        this.isoCode = isoCode;
    }
    private String isoCode;
    public String getIsocode() {
        return isoCode;
    }
    public void setIsocode(String isoCode) {
        this.isoCode = isoCode;
    }
}

5.4 客户端调用

public class Client {
    public static void main(final String[] args) {
        // JDK 8 Optional 对象判空示例
        // 具体对象
        User concreteUser = new User(new Address(new Country("china")));
        // 空对象
        User nullUser = new User(null);
        // 具体对象编码获取
        String concreteIsocode = Optional.ofNullable(concreteUser)
                .flatMap(u -> u.getAddress())
                .flatMap(a -> a.getCountry())
                .map(c -> c.getIsocode())
                .orElse("暂无").toUpperCase();
        // 空对象编码获取
        String nullIsocode = Optional.ofNullable(nullUser)
                .flatMap(u -> u.getAddress())
                .flatMap(a -> a.getCountry())
                .map(c -> c.getIsocode())
                .orElse("暂无").toUpperCase();
        System.out.println("Concrete User:" + concreteIsocode);
        System.out.println("Null User:" + nullIsocode);
    }
}

程序直接结果如下:

Concrete User:CHINA Null 

User:暂无

6、总结

在 Java 语言中,解决 NullPointerException 异常的常见方法是使用空对象模式,空对象模式可以省去代码中对 Null 值的判断,从而使代码更加的简洁和优雅。在 JDK 8 之后,Java API 给我们提供了 Optional 类,使用它可以优雅且有效的,规避空对象产生 NullPointerException 的问题。

传统的空对象模式通过服务端避免null值异常,Optional 从客户端使用避免null值异常。尤其是在需要嵌套获取值的情况下考虑使用Optional(文章开头的示例)。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值