王朝网络
分享
 
 
 

Null Object

王朝java/jsp·作者佚名  2006-01-30
宽屏版  字体: |||超大  

Null Object

Something for Nothing

Kevlin Henney

March 2003

kevlin@curbralan.com

kevlin@acm.org

[摘要]

Abstract

NULL OBJECT的意图就是通过提供一个可取代的并提供合适的默认do nothing行为的对象来封装那些不存在的对象。简而言之,就是提供一种"nothing will come of nothing" [Shakespeare1605]的设计模式.

NULL OBJECT是经过长时间反复探索而得到的一种很巧妙的设计模式,而且它不仅仅运用也面向对象(object-oriented)系统中,还以运用到空文件驱动(Unix系统中的 /dev/null 以及 Microsoft系统中的NUL)等等。

The pattern has been documented in a variety of forms by many authors, varying widely in

structure and length: from a thorough and structured GOF-like form [Woolf1998] to a brief

thumbnail-like production-rule form [Henney1997]. This paper is derived from a

previously published article [Henney1999] and includes the aforementioned thumbnail.

The aim of the current work is to update and capture the latest understanding of the

pattern and its implications, also addressing a wide audience by documenting the pattern

primarily with respect to Java and UML.

Thumbnail

if

; 一个对象的引用可能为空

; 这个引用必须在每次使用时检查是否为空

; 空对象结果就不做任何事情 或 分配一个合适的默认值

then

; 提供一个派生自这个对象的引用类型的类

; 实现它的所有方法并且使这些方法不做任何事情 或 提供一个默认的结果

; 当对象的引用可能为空时,使用这个类的实例

[问题 ]

Problem

给定的对象引用可能为空,如果检查结果引用为空,则就不去做任何事情 或 使用一些默认值。但是,如何使这个不存在的对象—— 可能存在空引用的对象 ——被显而易见的使用呢?

[范例 ]

Example

考虑为一个简单的服务service提供一个简单的日志。它可能被用作纪录事件的异常、管理的行为以及纪录在日常操作过程中的结果。你可能想象出许多不同类型的日志,例如直接向控制台输出的日志 或 使用RMI(Remote Method Invocation(远端方法呼叫) )将信息发送到远程的日志服务器。然而,一个服务器并不是必须使用日志的,所以服务器与日志之间的连接时任意的。[图一] 显示了在 UML 中类图之间的关系,[Listing 1]显示了一个Log接口和两个简单的实现。

图一.一个服务的日志的UML类图

public interface Log

{

void write(String messageToLog);

}

public class ConsoleLog implements Log

{

public void write(String messageToLog)

{

System.out.println(messageToLog);

}

}

?interface?

public class FileLog implements Log

{

public FileLog(String logFileName)

{

try

{

out = new FileWriter(logFileName, true);

}

catch(IOException caught)

{

throw new RuntimeException("Failed to open log file: " + caught);

}

}

public void write(String messageToLog)

{

try

{

out.write("[" + new Date() + "] " + messageToLog + "\n");

out.flush();

}

catch(IOException caught)

{

throw new RuntimeException("Failed to write to log: " + caught);

}

}

private final FileWriter out;

}

[Listing 1]一个Log接口和两个简单的实现

在[Listing 2]当中,可以看出在每次在使用Log对象之前都要反复检查对象是否为空——Null 。

public class Service

{

public Service()

{

this(null);

}

public Service(Log log)

{

this.log = log;

... // other initialization

}

public Result handle(Request request)

{

if(log != null)

log.write("Request " + request + " received");

...

if(log != null)

log.write("Request " + request + " handled");

...

}

... // other methods and fields

private Log log;

}

[Listing 2] I在服务中初始化、使用log对象

[力量 ]

Forces

这是一个在程序上对于类似于

if(log != null)

log.write(message);

代码很好的一个解决方案!

这种类型使得代码重复并倾向于错误:反复的检查对象是否为Null引用使得代码很混乱以及不清晰;也很容易忘记写Null引用的检验。然而,用户需要察觉这种情况,然后采取适当的怠惰,尽管这种情况并不存在于任何情况。但拥有一个空[Null]日志对象是我们所期待的状态。这样使得使用日志对象变得很简单、统一。

if(log != null)

log.write(message);

使用是有明确条件的,这意味着使用者可以根据上下文具体情况来选择操作。然而,如果操作通常是相同的或者这种选择的关系被频繁地使用。代码的重复被认为有”坏味道” 。"bad smell" [Fowler1999] 这些副本的工作通常只有很少部分不同,例如整理和改进,并且违反了DRY原则[(Don't Repeat Yourself) [Hunt+2000]]。

对于空引用的外在条件在执行数量上的消耗并不比测试或分支多,另一方面,使用外在的测试、检测意味着必须一直检测,因为这种检测在分开的代码中被不断的重复,不能设置编译的断点进行编译 或 介绍诊断的陈述。

空[null]检测如果可以被忽略或隐藏,代码会变得更清晰!JVM已经通过访问每个对象引用来防止对象引用为空, 这可能是对使用这种技术的一种诱惑吧。(见[Listing 3]),这项技术对所使用的语言有特殊的要求,就是必须保证所有被访问的对象都经过检验。例如:Java 和 C# 。当没有这个保证时,则导致不合需要的(不明确的)结果。例如:C++ .

public class Service

{

...

public Result handle(Request request)

{

try

{

log.write("Request " + request + " received");

...

log.write("Request " + request + " handled");

...

}

catch(NullPointerException ignored)

{

}

}

...

}

Listing 3. Assuming a non-null log and ignoring any NullPointerException.

这样依靠于语言有两种缺陷。

; Style: 尽管普通建议"例外应该特别"[ "exceptions should be exceptional"]的含糊, 那些记录不在好像的给屏蔽那些对NullPointerException的依赖一清楚滥用空陈词滥调不这样。 采用这样的一种风格的正常的动力是作为性能微最优化; 这样的推理不申请列举3。

; Correctness: NullPointerException适合任何进入通过空行投,并且不只通过木材写。 忽视这样的例外运转废除真实的错误,用洗澡水把婴儿扔出去的明显的危险。 在检查反对空行在捕获物条款更远的uglifies 代码内的木材, 并且在任何场合仍然不提供一个保证一根无效的木材是犯人。 一个解决办法的所被需要的是离开希望空行的能力以便没有什么 - 而不是特别的事情 - 发生。 这个解决办法应该在运行时间非侵入,容易编码,和廉价。

[解决方案 ]

Solution

Provide something for nothing:一个类,符合需要引用对象的接口,并且实现它的所有方法,并使这些方法不去做任何事情 或 返回适当的默认值。当对象引用可能为空时,使用这个类的实例。图二显示了一个基本的、典型的NULL OBJECT。

NULL OBJECT也可以被完全在许多通常的模式或对象结构中使用:一个空TERATOR [Gamma+1995]不到任何地方;一个空COMMAND [Gamma+1995]做(或不去做)任何事情;在C 语言中,可以通过提供一个指向空函数的指针,来实现一个空的回调行为;一个空的集合不可以被修改;一个空STRATEGY[Gamma+1995]不提供任何算法行为。

NULL OBJECT不可以被用来作为空引用的替代者。它的目的是封装一些不存在的对象,在那里那次不在不对一个实际物体的用户深重要。 如果那些选择性有基本意思,以导致不同的行为,NULL OBJECT将不适当。 任何对一个运行时间类型检测的需要, 例如一种isNull 方法,表明不在是重要的而不是透明的,建议为空[NULL]而不是一个NULL OBJECT。

例如,一个图像编辑器,一个空[NULL]的颜色,导致透明的颜色,这种情况将是NULL OBJECT很好的应用。然而,未婚的状态最好的描述并不是与一个空的配偶对象在一起: 结婚是可选择的,并且作为单身真的不同。

For example, in a graphical editor, a null fill color leads to transparency and would be a

good use of a NULL OBJECT. However, the state of being unmarried is not best represented

with a null spouse object: marriage is optional, and being single really is different.

[解决方案 ]

Resolution

重新回到这个日志的例子当中,一个NULL OBJECT类——NullLog,可以被引入LOG层次,如一个空类[null class](见Listing 4)不做任何事情并且不需要很多的编码技能!!

public class NullLog implements Log

{

public void write(String messageToLog)

{

}

}

Listing 4.一个空Log类,拥有一个空的行为

NULL OBJECT的优点:一旦初始化,就可以很容易的使用这个简单的日志框架。目前可以保证一个Log对象是强制的,就是日志服务是多样性的。这种被加强的关系允许明确条件码被消除,记录更简单和更多的制服。 Polymorphism 从事对的责任选择,使透明的记录的存在(或者不在)(看见列举5)。

public class Service

{

public Service()

{

this(new NullLog());

}

public Service(Log log)

{

this.log = log;

...

}

public Result handle(Request request)

{

log.write("Request " + request + " received");

...

log.write("Request " + request + " handled");

...

}

...

private Log log;

}

Listing 5. Introducing a NullLog object into Service.

[图三]显示了设计的接口(基于Listing 4, Listing 5以及图一),以及它们之间是如何协调、各部分的角色

对图二的概括。

Figure 3. The relationship between the problem resolution and the roles described in Figure 2.

[推论]

Consequences

介绍 NULL OBJECT通过消除过多并且重复的非方法的核心逻辑的一部分来简化客户的代码。选择和变化是经过多态和继承表达,这要比程序上的条件测试要好的多。退一步来说,多态可以远离switch的声明或是if else if 的

嵌套来表现。在NULL OBJECT这个特殊的类中,虚拟(但隐藏)的空默认或者已经被捕获或隐藏。

对象关系从可选择到强制性的, 这使得使用的关系更加的统一。 然而, 为了保存这个固定量,就要

确保引用不可以被视为空:

; 对象的引用最终可能会被清除,所以只有在对象初始化时需要检查对象的引用,将NULL OBJECT设置在这个位置。

; 二者择一地,只有INDIRECT VARIABLE ACCESS [Beck1997] 被用来访问对象的引用。SETTING METHOD [Beck1997]可以确保引用被分配一个NULL OBJECT而不是一个空引用;GETTING METHOD [Beck1997]可以确保将一个空引用返回为一个NULL OBJECT 。

NULL OBJECT是被封装和内聚的:它做一件事情— 什么也不做 —它做的很好。它使行为(不存在的)更容易使用, 而且提供一个适当的位置给编译断点或打印陈述。

A NULL OBJECT is encapsulated and cohesive: It does one thing — nothing — and it does it

well. This reification of the void and encapsulation of emptiness eliminates duplicate

coding, makes the (absence of) behavior easier to use, and provides a suitable venue for

debug breakpoints or print statements.

一个NULL OBJECT上的任何副本的不存在意味着它的实例是不可变的,因此是可共享并且本质是线程安全的。一个NULL OBJECT典型的特点是它没有地域性, 这意味着所有的实例是同等的、一致的。 如果一定时间或空间需要一个单独的静态实例,可以使用SINGLETON object [Gamma+1995]。

另一方面,使用关系的一个NULL OBJECT意味那个方法的调用总是被运行,而且他们的arguments将会总是被评估。 对于通常的情形这不是一个问题, 但是将会总是有方法的一项可以确认的经常开支方法调用谁的arguments的评估是复杂的和昂贵的。

NULL OBJECT使用不依赖等级决定到远程对象。 如果一个Java 的 NULL OBJECT实现了 java.rmi.Remote 接口,每个方法的调用都会导致远程调用的巨大开支而且是导致失败的潜在点, 两者会淹没而且渐渐破坏那基本什么也不做的行为。 因此,如果在被分配的上下文中使用NULL OBJECT, 应优先选择传递null值,否则就要使用NULL OBJECT的副本。 在一分配了环境透明的回答,并非 透明的分享变成优先权。 因为 RMI 引入一项重要的经常开支, 因为一个NULL OBJECT类是很小的以及载入它将会是相对地廉价的。 对代码的唯一变化就是使NULL OBJECT实现 java.io.Serializable接口:

public class NullLog implements Log, Serializable

{

...

}

有很多其他的方法去执行doing nothing,可以引入更多的 NULL OBJECT 类。如可以使用EXCEPTIONAL VALUE object [Cunningham1995].,与doing nothing不同,当一个方法被调用时,EXCEPTIONAL VALUE会引发一个异常或者返回一个EXCEPTIONAL VALUE. ;SPECIAL CASE [Fowler2003]——当一个对象处于特殊的状态或情况时,如EXCEPTIONAL VALUE 或 NULL OBJECT ,实现 do nothing 是很简单的!然而,不同的参数传递模式需要不同的响应:

because the

Null Object

10 of 10

subclass does not truly conform to the superclass, failing the is a or is a kind of litmus test

for appropriate use of inheritance. The inclusion of redundant implementation that cannot

be disinherited suggests a tradeoff with weaker cohesion. However, inheritance from an

existing concrete class cannot be used if it or any of its methods are declared final.

Note that, for the same reasons of substitutability, a NULL OBJECT class should not be used

as a superclass except for other NULL OBJECT classes. The assumption is that any

inheritance of a NULL OBJECT class will preserve that classification — the subclass of a

NULL OBJECT class is also a NULL OBJECT class, and its instances can be used where any

NULL OBJECT is expected.

Acknowledgments

An earlier version of this paper was published in Java Report (RIP) [Henney1999]. The

current version has also benefited from earlier papers by Bruce Anderson [Anderson1996]

and Bobby Woolf [Woolf1998].

I would like to thank the following people: Joe Bergin for his shepherding and humor;

Hubert Matthews for his additional comments on the preshepherded version of this paper;

Frank Buschmann and the Siemens patterns retreat in St Martin, Austria, in June 2000 for

their company and comments on a previous draft of this paper; Jim Coplien, Pascal

Costanza, Jutta Eckstein, Ian Graham, Klaus Marquardt, Alan O'Callaghan, Andreas

Rüping, Titos Saridakis, Didi Schütz, Christa Schwanninger, Markus V?lter, and Uwe

Zdun for the comments they gave on the paper at EuroPLoP 2002.

References

[Anderson1996] Bruce Anderson, "Null Object", PloP '96.

[Beck1997] Kent Beck, Smalltalk Best Practice Patterns, Prentice Hall, 1997.

[Buschmann+1996] Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad,

and Michael Stal, Pattern-Oriented Software Architecture, Volume 1: A System of Patterns,

Wiley, 1996.

[Cunningham1995] Ward Cunningham, "The CHECKS Pattern Language of Information

Integrity", Pattern Languages of Program Design, edited by James O Coplien and Douglas

C Schmidt, Addison-Wesley, 1995.

[Fowler1999] Martin Fowler, Refactoring: Improving the Design of Existing Code, Addison-

Wesley, 1999.

[Fowler2003] Martin Fowler, Patterns of Enterprise Application Architecture, Addison-Wesley,

1999.

[Gamma+1995] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design

Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995.

[Henney1997] Kevlin Henney, "Java Patterns and Implementations", presented at BCS

OOPS Patterns Day, October 1997, http://www.curbralan.com.

[Henney1999] Kevlin Henney, "Patterns in Java: Something for Nothing", Java Report 4(12),

December 1999, http://www.curbralan.com.

[Hunt+2000] Andrew Hunt and David Thomas, The Pragmatic Programmer, Addison-

Wesley 2000.

[Schmidt+2000] Douglas Schmidt, Michael Stal, Hans Rohnert, and Frank Buschmann,

Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked

Objects, Wiley, 2000.

[Shakespeare1605] William Shakespeare, King Lear, 1605.

[Woolf1998] Bobby Woolf, "Null Object", Pattern Languages of Program Design 3, edited by

Robert Martin, Dirk Riehle, and Frank Buschmann, Addison-Wesley, 1998.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有