Null Object

zhaozj2021-02-16  56

Null Object

Something for Nothing

Kevlin Henney

March 2003

Kevlin@curbralan.com

Kevlin@acm.org

Abstract

The intent of a null Object is to encapsulate the Absence of an Object by providing a

Substitutable Alternative That Offers Suitable Default Do Nothing Behavior. in Short, A Design

Where "Nothing Will Come Of Nothing" [shakespeare1605].

Null Object Is A Tactical Pattern That Has Been Discovered Time and Again, And Not Only in

Object-Oriented Systems: Null File Devices (/ dev / null on unix Nul on Microsoft Systems),

NO-OP MACHINE INSTRUCTIONS, TERMINATORS ON Ethernet Cables, ETC.

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 deived from A

Previously Published Article [Henney1999] and incrudes 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.

Null Object

2 of 10

Thumbnail

IF

.. an Object Reference May Be Optionally Null and

.. this Reference Must Be Checked Before Every Use and

.. The result of a null check is to do nothing or assocign a suitable default value

THEN

.. provide a class deact from the object reason's type and

.. Implement All Its Methods To Do Not, or Provide Default Results and

.. Use an instance of this class WHENEVER THE Object Reference Would Have Been Null

Problem

Given That An Object Reference May Be Optionally Null, And That The Result of A Null Check IS

To do nothing or us, how can the Absence of an object - the PresenceOf a Null Reference - Be Treated Transparently?

EXAMPLE

Consider a logging facility for some kind of simple server-housed service. It can be used to

Record Exceptional Events, Housekeeping Activities, And The Outcome of Operations During

The Course of the Service's Operation. One Can Imagine Many Different Kinds of Log, Such AS

A log that Writes Directly to the console or one this uses rmi to send a message to a remote

Logging Server. However, a Server Is Not Necessarily Required To Use A Log, So The Association

Between the Server and the log is optional. Figure 1 shows The Relationships diagrammed in

UML, AND Listing 1 Shows The Log Interface and Two Simple Implementations.

Figure 1. UML Class Diagram of a service with optional support for logging.

Public Interface log

{

Void Write (String Messagetolog);

}

Public Class Consielog Implements LOG

{

Public void write (String MessageTolog)

{

System.out.println (Messagetolog);

}

}

.interface.

Log log

0..1

Service

File

Log

Console

Log

Null Object

3 of 10

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. THE ROOT LOG Interface and Sample Concrete Implementations.in LISTING 2, IT CAN BEEN THAT BECAUSE THE OPTION EXSTS for Not Having Logging Enabled for

A Service, a Check Against Null Is Required Before Every Use of log.

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. INITIALIZING AND Using A Log Object in The Service.

Null Object

4 of 10

FORCES

There Is A Great Deal of Procedural Clunkiness In Code Such AS

IF (log! = null)

Log.Write (MESSAGE);

This Style Is Repetitive As Well As Error Prone: Reference CHECKS for Null References Can Clutter

And Obfuscate Code; it is to easy to forget to write the null guard. however, the user is

Required to Detect The Condition and Take Appropriate Inaction, Even Though The Condition

Is Not in Any Way Exceptional - Having a Null Log Is A Normal and Expected State of The

Relationship. The Condition Makes The Use of the Logging Facility Less Uniform.

A feature of conditional code is the explicitness of its conditions and the visibility of there

Resulting Control Flow. However, this is only a benefitiffiff f The decisions taken isportant

To the logic of the surrounding code, Otherwise the resulting code is less Than

More - Direct. Such Minor But Essential Decisions Become Distractions Rather Than

Attractions, Obscuring The Core Algorithm.

WHERE CONDITIONAL COGIC, MAKING THE Method. This

Is Especially True If The Code Is Repeated Frequently, As One Might Expect from The Logging

Facility In the Example, or if in-house coding guidelines encourage the use of blocks to

Surround Single Statements:

IF (log! = null)

{

Log.Write (MESSAGE);

}

The use of an expedition Conditional Means That The User May Choose ALTERNATIVE ACTIONS To SUIT

The context. howeever, if The action is always the size and if the optional rellationship is

FREQUENTLY Used, As One Might Expect of Logging, IT Leads to Duplication of The Condition and

ITS Action. duplicate code is considered to have a "bad smell" [fowler1999]. duplication

Works against Simple Changes, Such As Fixes or Improvements, and is in violation of the

Dry PrinciPle (Don't review yourself) [Hunt 2000].

For An Explicit Conditional On A Null Reference The Cost of Execution Amounts To no more

THAN A TEST AND A Branch. on the other of an expedition Test Means That there Will

ALWAYS Be a test. Because this test is used in Separate PIECES OF CODE IS NOT PIECES OF CODE IS NOT PIECLE TO

Set Debugging Breakpoints, or Introduce Diagnostic Statements, Consistently for All Uses of

The Null Case.

The Code Would Be Much Cleareer If The Null Tests Could Be tent or hidden. The jvm

Already Checks Each Access Via Any Reference Against Null, And There Can Be a Temptation To

Exploit this behavior (See Listing 3). This Technique is Specific To Languages ​​That Guarantee

That All Accesses Are Checked, E.G. Java and C #, and Will Lead to undesirable (undefined)

Results where this Guarantee IS ABSENT, E.G. C . Null Object

5 of 10

Public Class Service

{

...

Public Result Handle (Request Request)

{

Try

{

Log.Write ("Request" Request "Received");

...

Log.write ("Request" Request "Handled");

...

}

Catch (NullPointersRexception Ignored)

{

}

}

...

}

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

There Are Two Objections to Such Piggybacking. There Is The Matter Of Style and Taste, AS

Well as the cost of overgetness at the expense of correctness:

.. style: in spite of the vagueness of the commit advice That "Exceptions Should Be

Exceptional ", The Reliance ON NullpointersReption for Masking the Absence of Logging

Does Seem to be a clear abuse of what is otherwise a hollow place gratitude. The Normal

Motivation for Adopting Such A Style Is as A Performance Micro-Optimization; Such

Reasoning does Not Apply to Listing 3.

.. Correctness: a Nullpointers: a NullPointers: a NullPointers: Via Null, And Not Just

Writing through log. Ignoring Such Exceptions Runs The Obvious Risk of Quashing

Genuine Errors, throwing the baby out with the bath water. To check log against null in

The catch clause further uglifies the code, And Still Does Not Offer a Guarantee in All Cases

That a null log was the culprit.

What is needed of a solution is The Ability to wish Away the Null So That Nothing - Rather

Than Something Exceptional - Happens. The Solution Should Be Non-Intrusive, Easy To

Code, and inexpensive at runtime.

Solution

PROVIDE SOMETHING for NOTING: a class this conforms to the interface required of the object

Reference, implementing all of its methods to do not not return suitable defaultvalues. Use an instance of this class when the object reference 10 otherwise has

Null. Figure 2 Shows An Essential, Typical Null Object Configuration AS Class Model. Other

Expressions of this Pattern is, of course, possible.

A null Object Can Be Used to Complete Many Other Common Patterns and Object

Structures: a Null Iterator [Gamma 1995] Goes Nowhere; a Null Command [Gamma 1995]

Does (and undoes) Nothing; a Pointer to an Empty Function Can Be Used to Provide Null

Callback Behavior In C; A Null Collection Is Empty and Cannot Be Changed; A Null Strategy

[Gamma 1995] Province no algorithmic behavior, a generalization That Extends to Compiletime

Binding of Template Policies IN C , E.G. Non-Locking Behavior for Strategized

Locking in a Single-Threaded ENVIRONMENT [SCHMIDT 2000]; A Null Lamina Can Terminate

Layers [Buschmann 1996], Such As a Null Socket Layer for A Standalone Workstation.

Null Object

6 of 10

A Null Object SHOULD NOT BE Used Indiscriminately As a Replacement for Null References. ITS

INTENT is to encapsulate the Absence of an Object Where That ABSENCE IS Not Profoundly

Significant to the user of an actual object. if The OptionAlity Has Fundamental Meaning That

Leads to Different Behavior, a Null Object Would Be An INAPPRIAT. Any NEED for A

Runtime Type Query, Such As An Isnull Method, Indicates That Absence IS Significant Rather

.

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 Reperesentedwith A Null Spouse Object: Marriage IS Optional, And Being Single Really Is Different.

Figure 2. KEY ROLES IN A Typical Null Object Collaboration.

Resolution

Returning to the Server-logging Example, a Null Object Class, Nulllog, Can BE Introduces

INTO The log hierarchy. Such a comfortably null class (see listing 4) does nothing and does

Not Demand A Great DEAL OF CODING SKILL!

Public Class Nulllog IMPLEMENTS LOG

{

Public void write (String MessageTolog)

{

}

}

Listing 4. a Nulllog Class Providing do Nothing Behavior.

THE TACTICAL BENEFIT OF A NULL Object, Once Initialized, IS in Simplifying The Use of this

Simple logging framework. it is now guarance That The Relationship to a log Object IS

MANDATORY, I.E. The Multiplicity from the service to the log is 1 rather Than 0..1. this

Strengened rellationship allows the expeneit conditional code to be eliminated, MAKING

Logging Simpler and More Uniform. Polymorphism Takes Up The Responsibility for Selection,

Making The Presence (or Absence) of Logging Transparent (See Listing 5).

.interface.

Interface

1

Client

Actual

Object

NULL

Object

Method

Method Method Do Not Nothing

Null Object

7 of 10

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 Null Object Into Service.

Figure 3 Shows The Classes and Interfaces in The Design (Based on Listing 4, Listing 5, Andadditional Classes from Figure 1) Andhow Correspond, in Terms of Roles, To The

Elements Outlined in Figure 2.

Figure 3. The Relationship Between The Problem Resolution and The Roles Described in Figure 2.

Consequences

Introducing a Null Object Simplifies The Client's Code by Eliminating Superfluous and SUPERFLUOS

REPEATED CONDITIONALS THATER NOT PART OF A METHOD'S CORE LOGIC. Selection and Variation Are

Expressed THROUGH POLYMORPHISM AND inheritance Rather Than Procedural Condition

Testing. Taking a Step Back, Polymorphism Can Be Seen to Magic Away Switch Statements OR

IF Else if Cascades. in The Specific Case of Null Object, The Missing (But Implied) EMPTY

Default or else Has been Captured and Concealed.

Service

File

Log

Console

Log

Log

NULL

Log

Nullobject

Client Interface

Actual

Object

Actual

Object

NULL

Object

Null Object

8 of 10

The Object Relationship Moves from Being Optional To Mandatory, Making The Use of the Of THE

Relationship More Uniform. However, To Preserve this Invariant, Care Must Be Taken To

Ensure That The Reference Is Never Seen As a Null:

.. The Reference May Be Decilad Final So That Only The Initialization Is Required To Guard

Against a Null Reference, Setting A Null Object In Its Place.

.. Alternative, ONLY Indirect Variable Access [Beck1997] Should Be Used to Access To

Reference. a setting method [beck1997] can Ensure That The Reference IS Assigned A

Null Object Rather Than A Null Reference, or a getting method [beck1997] can Ense

That a Null Reference Is Returned As a 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.

The Absence of Any Side Effects on a Null Object Means That Instances Are Immutable, And

AreateFore Shareable and Intrinsically Thread Safe. A Null Object Is Typically Stateless

And ITENTINTITY IS NOT A SIGNIFICANT Part of Its Make Up, Which Means That All Instances Are

Equivalent. if a time or space optimization is required a single static instance, but not

Necessarily A Singleton Object [Gamma 1995], May BE Used in Place of Freshly Instantiated

Null Objects.

On The Other Hand, Using A NULL OBJECT IN A RELATIONSHIP Means That Method Calls Will

Always Be Executed and Their Arguments Will Always Be Evaluated. for The Common Case

This Is Not a Problem, But There Will Always Be An Identifiable Overhead for Method Calls

Whose Argument Evaluation IS Complex and Expensive.

The Use of Null Object Does Not Scale To Remote Objects. If A Java Null Object WERE TO

Implement java.rmi.remote, EveryMeth Call Would Incur The Overhead of a Remote Call

And Introduce A Potential Point of Failure, Both of Which Wouldh wouldh wouldh wouldering

Basic do nothing behavior. Therefore, if used in a distributed context, Either Null Should Be

Passed in Preference To a null Object or the null Object shouth be passed by copy. in A

Distributed Environment Transparent Replication Rather Than Transparent Sharing Becomes

For RMI this Does Not Introduce A Significant Overhead, Because A NULL OBJECT

Class Is Small and Loading It Will Be Relatively Cheap. The Only Change To The Code IS To

Ensure That the Null Object Class Implements Java.io.Serializable: Public Class Nulllog Implements Log, Serializable

{

...

}

Users of a hierarchy That Includes a Null Object Class Will Have More Classes To Take ON

Board. The beneftion of the point of create, explicit knowledge of create

The New Null Object Class is Not needed. where there is man waiting,

More Null Object Classes Can Be Introducesd. A Variation ON this theme is the use of an

Exceptional Value Object [Cunningham1995]. Rather Than Doing Nothing When a Method

IS Called, An Exceptional Value Either Raises An Exception Or Returns A Further

Exceptional value. A generalization of this theme is the special case [fowler2003],

WHERE An Object In Some Way Repesents a Special Case, Such as an Exceptional Value ORUE OR A

Null Object, Each of Which Can Be Considered Special Cases of Special Case.

THE IMPLEMENTATION OF Do Nothing Behavior Is Simple for Methods: EMPTY METHOD

Bodies. However, Methods That Return Results Can Only Avoid The INEVITABLE RETURN

Null Object

9 of 10

Statement by throwing an exception - Behavior More Appropriate for An Exceptional

Value Than for a Null Object. Different Parameter Passing Modes Require Different

Responses:

.. in arguments: arguments passed to the method to provide it with suitable information

Can Be Safely Ignored, Requiring No Handling Code in The Method Body. Pass by Copy IS

Java's Sole Mechanism for Passing Arguments. In Corba the in Mode Corresponds To

In C Pass by Copy and Pass by Const Reference Play THIS ROLE. In C # The

IN Mode Is The Default for Method Arguments.

.. void Result: no method code is required to deal with a void return type ... out arguments: an out argument virt, by definition, be set and so the method body

Must set it to an appropriate default. Java Does Not Support these Directly, But The THE

Holder IDiom is offen buy to emulate it. This can is buy in the mapping of corba's

Out arguments. in c # an out argument must also be set.

.. Non-Void Result: As with out arguments, an appropriate default must be returned.

.. in-out arguments: an in-out argument may be each either ignored, as with an an in argument,

Or set to an appropriate default, as with an out argument. Java Only Supports in-Out

Arguments Via the Holder IDiom, As Found in The Mapping of Corba's Inout

Arguments. The Corresponding Feature IN C is pass by reference. in c # Ref

Arguments Fulfil this role.

However, what precisely is meant by "appropriate default"?

.. Success Defaults: a success value is offen an identity value of some kind and does not

Indicate Any Form Of Failure, And Will Typically Not Affect The Caller. For Numeric Types, 0

IS OFTEN Such a Value. For Result Objects That Represent Values, A Null Reference IS

Sometimes Suitable, But More offten a Default Constructed Object IS A Better Result, E.g. ""

As opposed to null for a string. For Result Objects That Repesent Behavior, Either a Null

OR a NULL Object of The Result Type Should Be Returned.

.. Failure Defaults: a Failure Value Shows That A Method Call Was in Some Way

Unsuccessful, But in The Context of Null Object It Should Probably Not Be Fatal. for

Integer Types, -1 is Offen Used to Signal Failure. For floating point type, nan or infinity

Are The Common Out-of-Band Results. For Objects, Either A Null Reference or Anexceptional Value Should Be Returned.

IF no defaults area required - E.g. a method taking no arguments or in arguments only and

Returning void - Or Results Can Be Default Constructed - E.g. a returnofa of 0 for A

Numeric OR a NULL for a Reference - a Dynamic Proxy Can Be Used As a Generalized NULL

Object. Dynamic PROXIES Are A JDK 1.3 Feature (java.lang.reflect.Proxy) and, based on

Reflection, They Can Interpret Arbitrary Messages. A Null Object Dynamic Proxy Would

SIMPLY Discard All Method Requests, Returning Default Values ​​Where Necessary.

Taking an existing application of code trias not useful object and modify it to do so

May Also Open the dor to bugs. by Following Carefully the Introducture Null Object

Refactoring [Fowler1999] Programmers Can Have Greater Confidence Making Such Changes

And Avoiding Pitfalls.

WHEN Introducing a Null Object Class Into An EXISTING SYSTEM A SuiTable Base Interface

May Not Exist for the Null Object Class to Implement. Either An Extract Interface

Refactoring [Fowler1999] Should Be Applied to Introduce One or The Null Object Class Must

Subclass the concrete class referred to by the reference. The second option allow becom

Unavoidable if the hierarchy code cannot Be Rearrany Code Cannot Be Rearrani

Option: IT Implies That Any Reperesentation In The Superclass Will BE IGNORED. SUCH

Inheritance with cancellation can list to code what is harder to understand Because the

转载请注明原文地址:https://www.9cbs.com/read-20781.html

New Post(0)