设计模式一个要点就是可以封装变化点,使得这个变化点上的的扩张变可以变得很轻松。Null Object Pattern却不是。它没有封装任何变化点,甚至它的结构图仅仅就是一个简单的类继承结构: 所以以其说它是一个设计模式倒不如说是一个编程技巧。 下面我们来看几个实例: 1.我们来看一个简单的屏幕保护程序,它的功能就是在屏幕上显示一个可以移动且颜色可以变化的圆,假设移动和颜色变化是变化的方向。我们首先想到的就是用策略模式分别表示它颜色和移动。如下图: 根据依赖倒置原则我们可能先会设计Ball的逻辑,然后再设计IMotionStrategy和IColorStrategy。若我们只实现了继承于IMotionStrategy的类1,这个时候却想看看效果,一个常用的方式就是用最简单的方法实现一个IColorStrategy的子类,这个子类什么也没做。这样我们会看到一个拥有默认颜色的球体按我们类1预定的轨迹移动。其实这个什么也没做的类就是我们的Null Object。由此引出它的第一个好处。利于框架的搭设和单方面功能测试。以后实现类3来替换也是很简单的,因为Null Object完全基于IColorStrategy接口。当然这里IMotionStrategy和IColorStrategy是正交的。
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,改一处就可以了,而且使得客户端可以一致的对待所处理的对象,因为NullLog和RealLog都继承于同一接口. 3.另外我们写代码的时候经常是 IMotionStreategy MotionWay = null; … If (MotionWay == null) { Throw new NullReferenceException (); } MotionWay.Run(); 若有一个NullObject我们就可以 IMotionStreategy MotionWay = NullMotion.CreateInstance(); … MotionWay.Run(); 完全可以不必当心会出现null的情况,程序中的null完全由Null Object对象代替。在Java和C#里面似乎是没有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来实现。 |
|
来自: scholes_goal > 《我的图书馆》