SDSU CS 635: Advanced Object-Oriented Design & Programming
Spring Semester, 1998
Strategy and Null Object

To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Apr-98

Contents of Doc 10, Strategy and Null Object


References slide # 1
Strategy slide # 2
...Intent slide # 2
...Applicability slide # 5
...Consequences slide # 6
...Implementation slide # 7
NullObject slide # 8
...Applicability slide # 9
...Consequences slide # 10
...Implementation slide # 11
...Smart Node - Ordered Linked List Example slide # 12
...... Sample Node Code slide # 15
Logger Revisited slide # 18



References


Design Patterns: Elements of Resuable Object-Oriented Software
, Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1995, pp 315-314

"Null Object", Woolf, in Pattern Languages of Program Design 3, Edited by Martin, Riehle, Buschmmann, Addison-Wesley, 1998, pp 5-18


Doc 10, Strategy and Null Object Slide # 2

Strategy


Intent

Define a family of algorithms, encapsulate each one, and make them interchangeable

Strategy lets the algorithm vary independently from clients that use it




Doc 10, Strategy and Null Object Slide # 3
Examples

Sorting
Different types of sorts have different characteristics

Shellsort

No extra space needed
Fast but not O(n*log(n))
Very fast on nearly sorted data
Does comparatively well on small lists

Quicksort
Average case is O(n*log(n))
Relatively poor performance on short lists
Requires a stack of ~ log(n) in depth
MergeSort
Worst case is O(n*log(n))
Requires O(n) extra space
Stable

Have a sorted list container, which one gives a sort algorithm

SortedList studentRecords = new SortedList( new ShellSort() );
studentRecords.add( "Sam" );
etc.

Doc 10, Strategy and Null Object Slide # 4
Pattern Matching

Finding a pattern in text is a common operation

Find the first occurrence of the word "NullObject" in this set of notes after this line of text.

There are various algorithms one can use:

Brute Force

Easy to implement
Bad worst case, but good performance in practice

KMP
Good worst case

Boyer-Moore
Excellent worst case
Very hard to implement

QuickSearch
Easy to implement
Good performance
Good worst case

State Machines
Very general

Could use a text object, that has a pattern search object

Doc 10, Strategy and Null Object Slide # 5

Applicability


Use the Strategy pattern when


€ you need different variants of an algorithm

€ an algorithm uses data that clients shouldn't know about

€ a class defines many behaviors, and these appear as multiple switch statement in the classes operations

€ many related classes differ only in their behavior


Doc 10, Strategy and Null Object Slide # 6

Consequences


Families of related algorithms

Alternative to subclassing of Context

What is the big deal? You still subclass Strategy!

Eliminates conditional statements

Replace in Context code like:

          switch  ( flag ) {
               case A:     doA();     break;
               case B:     doB();     break;
               case C:     doC();     break;
          }

With code like:

          strategy.do();


Gives a choice of implementations

Clients must be aware of different Strategies

SortedList studentRecords = 
new SortedList( new ShellSort() );
Communication overhead between Strategy and Context

Increase number of objects


Doc 10, Strategy and Null Object Slide # 7

Implementation


Defining the Strategy and Context interfaces

How does data flow between them

Context pass data to Strategy

Strategy has point to Context, gets data from Context

Strategies as template parameters

Can be used if Strategy can be selected at compile-time and does not change at runtime

Making Strategy objects optional

Give Context default behavior

If default used no need to create Strategy object


Doc 10, Strategy and Null Object Slide # 8

NullObject



NullObject implements all the operations of the real object, but these operations do nothing


Doc 10, Strategy and Null Object Slide # 9

Applicability


Use the Null Object pattern when:

Some collaborator instances should do nothing

You want clients to ignore the difference between a collaborator that does something and one that does nothing - so the client does not have to explicitly check for null or some other special value

You want to be able to reuse the do-nothing behavior so that various clients that need this behavior will consistently work in the same way

Use a variable containing null or some other special value instead of the Null Object pattern when:

Very little code actually uses the variable directly

The code that does use the variable is well encapsulated - at least in one class

The code that uses the variable can easily decide how to handle the null case and will always handle it the same way


Doc 10, Strategy and Null Object Slide # 10

Consequences

Advantages

Uses polymorphic classes

Simplifies client code

Encapsulates do nothing behavior

Makes do nothing behavior reusable


Disadvantages

Forces encapsulation
Makes it difficult to distribute or mix into the behavior of several collaborating objects

May cause class explosion

Forces uniformity

Different clients may have different idea of what "do nothing" means

Is non-mutable
NullObject objects can not transform themselves into a RealObject

Doc 10, Strategy and Null Object Slide # 11

Implementation


Too Many classes

Eliminate one class by making NullObject a subclass of RealObject

Multiple Do-nothing meanings

If different clients expect do nothing to mean different things use Adapter pattern to provide different do nothing behavior to NullObject

Transformation to RealObject

In some cases a message to NullObject should transform it to a real object

Use the proxy pattern

3. Transformation to a RealObject


Doc 10, Strategy and Null Object Slide # 12

Smart Node - Ordered Linked List Example

Class Structure




Object Structure



Doc 10, Strategy and Null Object Slide # 13
Node
Node objects are used to create a linked list. A Node contains an integer and a pointer to the next Node in the list. It also has add and print operations.

StartNode
StartNode object is used as a dummy first Node in a linked list. EndNode and StartNode are used to make sure a list always has two elements, eliminating the need to deal with a list with one or zero Nodes. StartNode passes all messages it receives on to the next Node in the list.

EndNode
EndNode object is used as a dummy Node at the end of a linked list. EndNode and StartNode are used to make sure a list always has two elements, eliminating the need to deal with a list with one or zero Nodes. EndNode does not pass any message on to the next Node, since it is at the end of the list.

OrderedLinkedList
OrderedLinkedList is the interface class for linked lists. Main operations are create, add, and +. Client program uses this class. OrderedLinkedList uses Node, EndNode and StartNode to maintain the linked list. Client code does not know about Node, EndNode and StartNode.


Doc 10, Strategy and Null Object Slide # 14

NullObject & LinkedList

EndNode is an example of a NullObject

The add operation would be cleaner if the EndNode could transform itself into a Node

Is StartNode a NullObject?


Doc 10, Strategy and Null Object Slide # 15

Sample Node Code

Node.h
class Node {
public:
     int value;          /*content of Node*/
     Node *next;     /*pointer to next Node*/

     Node();
     Node(int initialValue);

     virtual void print();     
     virtual int operator<(const Node &a);

     virtual void add(Node &a);
};

/*Class Comment

Requires subclasses: EndNode and StartNode 

List containing a Node must start with StartNode and end with
EndNode.
 
Keep StartNode and EndNode in their proper locations by using
the Node operator < to compare the contents of cells.  Do not
access the contents of member variable value to determine
where a Node belongs in a list.

Print, and add, are recursive type calls.  Node processes the call,
then passes it on to the next Node.  EndNode ends these
recursive calls.
*/


Doc 10, Strategy and Null Object Slide # 16
Node.cc
#include <stream.h>
#include "Node.h"


//Effects: 
//          Returns new Node with pointer set to null
Node::Node() 
{
     next = 0;
};

//Effects:  
//          Return new Node with contents = initialValue
Node::Node(int initialValue)
{
     value = initialValue;
     next =  0;
};


//Requires: 
//          This is a Node in an ordered (ascending) linked list
//          This Node < a
//Effects:
//          Inserts a in proper location in ordered linked list
void Node::add(Node &a)
{
     if (*next < a )      
          next->add(a);
     else      
          {a.next = next;   next = &a;};
};


Doc 10, Strategy and Null Object Slide # 17
//Requires:
//          Value has been set in this Node and a
//Effects:
//          Returns true if this Node < a
int Node::operator<(const Node &a) 
{
     return value < a.value;
};


//Requires:
//          This Node is in linked list terminated with EndNode
//Effects:
//          Prints, on standard output, value of this Node and all 
//           cells after this
void Node::print()                         
{
     cout << value << " " ;
     next->print();
};


Doc 10, Strategy and Null Object Slide # 18

Logger Revisited


What patterns are used here?


visitors since 24-Feb-98