空对象模式

概述

在这个快速教程中,咱们将看一下Null对象模式,这是设计模式的一个特例,一个常常被咱们遗忘的特例。咱们将描述它的目的以及咱们什么时候应该考虑使用它。编程

像往常同样,咱们还将提供一个简单的代码示例。设计模式

空对象模式

在大多数面向对象的编程语言中,咱们会考虑不进行null引用,为此咱们常常被迫写空检查:
Command cmd = getCommand();
if (cmd != null) {
    cmd.execute();
}

有时,若是此类if语句的数量变多,则代码的可读性会变得不好,难以阅读且容易出错。这是Null对象模式能够派上用场的时候。编程语言

Null对象模式的目的是最小化这种空检查。相反,咱们能够识别null行为并将其封装在客户端代码所指望的类中。这样咱们就再也不须要处理空引用的特殊处理。ide

咱们能够简单地处理null对象,就像处理实际包含一些更复杂的业务逻辑的给定类型的任何其余实例同样,以此来保持代码的清洁。spa

因为null对象不该具备任何状态,所以无需屡次建立相同的实例。所以,咱们常常将null对象实现为单例设计

Null Object Pattern的UML图

让咱们直观地看一下模式:code

模式图

咱们能够看到,会有如下参与者:router

  • 客户端须要AbstractObject的实例
  • AbstractObject定义客户指望的执行逻辑- 它还能够包含实现类的共享逻辑
  • RealObject实现了AbstractObject并提供了真实的行为
  • NullObject实现AbstractObject并提供do nothing行为

案例

如今咱们已经清楚地了解了这个理论,让咱们看一个例子。对象

假设一个场景,有一个消息路由器模块。每条消息都应分配有效的优先级。咱们的系统应该将高优先级消息路由到SMS网关,而具备中等优先级的消息应该路由到JMS队列。教程

可是,有时会出现“未定义”或空优先级的消息。这些消息应该从进一步处理中丢弃。

首先,咱们将建立路由器接口:

public interface Router {
    void route(Message msg);
}

接下来,让咱们建立上述接口的两个实现 - 负责路由到SMS网关的接口以及将消息路由到JMS队列的接口:

public class SmsRouter implements Router {
    @Override
    public void route(Message msg) {
        // implementation details
    }
}
public class JmsRouter implements Router {
    @Override
    public void route(Message msg) {
        // implementation details
    }
}

最后,让咱们实现咱们的null对象

public class NullRouter implements Router {
    @Override
    public void route(Message msg) {
        // do nothing
    }
}

咱们如今准备将全部部分组合在一块儿。让咱们看看示例客户端代码的外观如何:

public class RoutingHandler {
    public void handle(Iterable<Message> messages) {
        for (Message msg : messages) {
            Router router = RouterFactory.getRouterForMessage(msg);
            router.route(msg);
        }
    }
}

咱们能够看到,不管RouterFactory返回什么实现,咱们都以相同的方式处理全部Router对象。这使咱们可以保持代码的清洁和可读性。

什么时候使用空对象模式

咱们应该使用Null对象模式,不然客户端会检查null只是为了跳过执行或执行默认操做。 在这种状况下,咱们能够将do nothing逻辑封装在空对象中,并将其返回给客户端而不是空值。这样,若是给定实例为null,则客户端代码再也不须要知道 。

这种方法也遵循通常的面向对象原则。

为了更好地理解什么时候应该使用Null对象模式,让咱们假设咱们必须实现CustomerDao接口,定义以下:

public interface CustomerDao {
    Collection<Customer> findByNameAndLastname(String name, String lastname);
    Customer getById(Long id);
}

若是没有客户匹配提供的搜索条件,大多数开发人员将从findByNameAndLastname()返回Collections.emptyList()。这是遵循Null对象模式的一个很好的例子。

相反,getById()应返回具备给定id的客户。调用此方法的人但愿得到特定的客户实体。若是不存在这样的客户,咱们应该显式返回null以表示提供的ID存在问题。

与全部其余模式同样,咱们须要在盲目实现Null对象模式以前考虑咱们的特定用例。不然,咱们可能会无心中在代码中引入一些很难找到的错误。

结论

在本文中,咱们了解了Null对象模式是什么以及什么时候能够使用它。咱们还实现了一个设计模式的简单示例。

关注油腻的Java