空对象模式

来源:互联网 发布:国研网数据子库 编辑:程序博客网 时间:2024/05/01 17:47

相信大家一定在开发中见过并且写过类似这样的代码:

[java] view plain copy
  1. public Book getBook(int id) {  
  2.     if (id < 0) {  
  3.         return null;  
  4.     }  
  5.     return new Book(1"Design Pattern"100);  
  6. }  
[java] view plain copy
  1. Book book = getBook(-1);  
  2. if (book != null) {  
  3.               
  4. }  

系统在使用对象的相关功能时,总要检查对象是否为null,如果不为null,我们才会调用它的相关方法,完成某种逻辑。这样的检查在一个系统中出现很多次,相信任何一个设计者都不愿意看到这样的情况。为了解决这种问题,我们可以可以引入空对象,这样,我们就可以摆脱大量程式化的代码,对代码的可读性也是一个飞跃。

等等,空对象是什么?它和null什么关系?

空对象是一个没有实质性内容的对象,但他并不为null。你可以把空对象理解为一个空箱子,这个物品还是存在的,只不过仅仅是一个壳,没有实质性的东西。

我们需要对原有的代码进行重构,把所有返回null的地方都替换成返回一个与之对应的空对象,然后再客户端调用时不再使用book==null这种方式判断是否为空,而是替换成book.isNull()的方式。下面我们就一步一步来实现这种模式。

首先,我们需要定义一个Nullable接口:

[java] view plain copy
  1. public interface Nullable {  
  2.     /** 
  3.      * 对象是否为空 
  4.      * @return 
  5.      */  
  6.     public boolean isNull();  
  7. }  

这个接口定义了一个isNull()的方法,来表示一个对象是否为空。

然后我们让Book实现此接口:

[java] view plain copy
  1. public class Book implements Nullable {  
  2.   
  3.     private int id;  
  4.     private String name;  
  5.     private double price;  
  6.       
  7.     public Book() {  
  8.     }  
  9.       
  10.     public Book(int id, String name, double price) {  
  11.         this.id = id;  
  12.         this.name = name;  
  13.         this.price = price;  
  14.     }  
  15.       
  16.     @Override  
  17.     public boolean isNull() {  
  18.         return false;  
  19.     }  
  20.   
  21.     /** 
  22.      * 创建一个NullBook实例代表空对象 
  23.      * @return 
  24.      */  
  25.     public static Book createNullBook() {  
  26.         return new NullBook();  
  27.     }  
  28.       
  29.     /** 
  30.      * setters & getters 
  31.      */  
  32. }  

在Book中实现了isNull()方法,并返回false;另外Book还定义了一个静态的createNullBook方法,返回了一个NullBook的实例,NullBook是什么呢,我们来看一下:

[java] view plain copy
  1. public class NullBook extends Book {  
  2.     @Override  
  3.     public boolean isNull() {  
  4.         return true;  
  5.     }  
  6. }  

NullBook继承了Book,并在isNull()方法中返回true,这个NullBook其实就是我们上面提到的空对象,仅仅是个空箱子而已。

然后我们定义一个BookService类,用以模拟获取Book的接口方法:

[java] view plain copy
  1. public class BookService {  
  2.     public Book getBook(int id) {  
  3.         if (id < 0) {  
  4.             //返回一个空对象  
  5.             return Book.createNullBook();             
  6.         }  
  7.         return new Book(id, "Design Pattern"100);  
  8.     }  
  9. }  

在getBook(int id)中,如果id<0时,则返回一个NullBook的实例,在客户端调用isNull()方法时则返回true,表示是空对象;如果id>=0时,则返回一个Book的实例,在客户端调用isNull()方法时则返回false,表示对象不为空,可以调用相关方法取得数据。对于我们的客户端来讲,返回的都是一个Book类型的对象,我们并不清楚返回的到底是真正的Book实例还是空对象NullBook实例,但是我们并不担心,因为有一点可以肯定的是,我们都可以放心的调用isNull()方法,因为它会根据运行期对象的类型返回一个正确的值。

多态的最根本好处在于:你不必再向对象询问“你是什么类型”而后根据得到的答案调用对象的某个行为,你只管调用该行为就是了,其他的一切事情多态机制会为你妥善处理。

最后再来看一下客户端是如何实现对象为空的判断的:

[java] view plain copy
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.         BookService service = new BookService();  
  4.         Book book = service.getBook(-1);  
  5.         if (book.isNull()) {  
  6.             System.out.println("not found!");  
  7.         } else {  
  8.             System.out.println("name:" + book.getName());  
  9.             System.out.println("price:" + book.getPrice());  
  10.         }  
  11.     }  
  12. }  

 除了定义isNull()之外,我们还可以使用另外一种方式来完成“检查对象是否为null”的功能。

我们需要先定义一个Null接口,这个接口不需要定义任何方法,仅仅是标志着对象是否为空对象:

[java] view plain copy
  1. public interface Null {  
  2. }  

然后我们的Book类现在不再需要实现任何接口了,它仅仅是一个普通的JavaBean,我们只需新增一个这样的NullBook类:

[java] view plain copy
  1. public class NullBook extends Book implements Null{  
  2. }  

修改相应的方法:

[java] view plain copy
  1. public Book getBook(int id) {  
  2.         if (id < 0) {  
  3.             //返回一个空对象  
  4.             return new NullBook();            
  5.         }  
  6.         return new Book(id, "Design Pattern"100);  
  7. }  

然后在客户端这样使用:

[java] view plain copy
  1. if (book instanceof NullBook) {  
  2.               
  3. }  

通常,不在万不得已的情况下,我们应该尽量避免使用instanceof操作符,但在这种情况下,我们也提倡使用它,因为这种做法有一个好处就是,不需要修改Book源码,这么一来,即使无法修改Book,我们也可以使用空对象。

0 0
原创粉丝点击