SweetDream
高歌一壶新酿酒,醉抱青山不必归。

设计模式一个要点就是可以封装变化点,使得这个变化点上的的扩张变可以变得很轻松。Null Object Pattern却不是。它没有封装任何变化点,甚至它的结构图仅仅就是一个简单的类继承结构:

所以以其说它是一个设计模式倒不如说是一个编程技巧。

                         

下面我们来看几个实例:

1.我们来看一个简单的屏幕保护程序,它的功能就是在屏幕上显示一个可以移动且颜色可以变化的圆,假设移动和颜色变化是变化的方向。我们首先想到的就是用策略模式分别表示它颜色和移动。如下图:

根据依赖倒置原则我们可能先会设计Ball的逻辑,然后再设计IMotionStrategyIColorStrategy。若我们只实现了继承于IMotionStrategy的类1,这个时候却想看看效果,一个常用的方式就是用最简单的方法实现一个IColorStrategy的子类,这个子类什么也没做。这样我们会看到一个拥有默认颜色的球体按我们类1预定的轨迹移动。其实这个什么也没做的类就是我们的Null Object。由此引出它的第一个好处。利于框架的搭设和单方面功能测试。以后实现类3来替换也是很简单的,因为Null Object完全基于IColorStrategy接口。当然这里IMotionStrategyIColorStrategy是正交的。


           

2.有的时候我们程序需要写这样的代码来输出调试信息:

 

if (cond)

{

...

System.out.println("Cond is true.");

}

else

{

...

System.out.println("Cond is false.");

}

...

 

在没有类似c++

#ifdef _DEBUG

#endif

的时候我们只有在结束的时候把这段注释掉:

if (cond)

{

...

//System.out.println("Cond is true.");

}

else

{

...

//System.out.println("Cond is false.");

}

...

这是非常繁琐而且容易出错的。

一种解决方法是提供一个OutPutWriter对象,它可以把需要的调试信息输出。如果你不需要输出的时候把这个对象置为null

// Some class:

public class MyClass

{

private OutputWriter log = null;

public MyClass() {}

public MyClass(OutputWriter log)

{

this.log = log;

}

// in the body of some method:

...

if (cond)

{

...

if (log != null)

log.write("Cond is true.\n");

}

else

{

...

}

...

if (log != null)

log.write("Cond is false.\n");

}

}

这样做虽然可以避免在发布版本不输出调试信息。但还是有几个缺点无法避免:

a.  你必须用if语句段来确保对OutputWriter对象是否为null进行处理。

b.  没有一个一致的方法来处理得到的StringWriter对象。

 

这个时候就可以考虑使用NullObject模式了。

 

             

 

不需要输出调试信息的时候我们用NullLog来替代RealLog,改一处就可以了,而且使得客户端可以一致的对待所处理的对象,因为NullLogRealLog都继承于同一接口.

 

 

3.另外我们写代码的时候经常是

IMotionStreategy MotionWay  = null;

If (MotionWay == null)

{

    Throw new NullReferenceException ();

}

MotionWay.Run();

 

若有一个NullObject我们就可以

IMotionStreategy MotionWay = NullMotion.CreateInstance();

MotionWay.Run();

 

完全可以不必当心会出现null的情况,程序中的null完全由Null Object对象代替。在JavaC#里面似乎是没有null的异常了。注意,由于C++中所有变量并非为引用,所以还是可能出现指针异常,而且在C++中没有垃圾收集,构建的NullObject对象按道理不能被释放。

 

代码片段(Java)

// 感谢网友Goingmm 提供

public interface Person {

    

     /**

      * 让这个人喊叫指定的信息(message)

      * @param message

      * @return

      */

     public String shout(String message);

    

     /**

      * 1) 定义一个匿名内部类

      * 2) 这样做能确保在当前JVM中只有NULL的唯一实例

      * 3) 从效率和编码的优雅角度看,采用此策略都是可取的

      */

     public static final Person NULL = new Person(){

         public String shout(String message) {

              return "--对不起!我是NULL对象,我不会喊叫.";

         }

     };

}

 

测试代码:

public class TestNullObject {

    

     public static void main(String[] args) {

         // ①取得这个特殊的NULL对象

         Person obj = getPerson();

        

         // ②调用接口中提供的方法

         System.out.println(obj.shout("我是中国人!"));

     }

    

     public static Person getPerson() {

         return Person.NULL;

     }

}

 


总结:

Null Object模式中的Null Object总得来说并不是要求你必须以空语句段来实现,只不过空语句段是一个很常见得实现方式。它仅仅说明Null Object是个默认的实现对象。这个对象并没有作业务逻辑处理,是意义上的空实现。当然如果它功能有了一定意义那就有可能有背这个模式的初衷了。由于默认的实现对象常常是一样的,所以Null Object可以用Singleton来实现。

posted on 2006-12-09 17:12  SweetDream  阅读(919)  评论(0编辑  收藏  举报