[Tutor] Null Object Design Pattern Question [sentinels and Minesweeper]

Tim Johnson tim at johnsons-web.com
Mon Jun 14 18:56:58 EDT 2004


Thanks to all for the pointers and examples on the topic.
I believe you've given me a good handle on the NOP.

cheers
tim

* Danny Yoo <dyoo at hkn.eecs.berkeley.edu> [040614 00:20]:
> 
> On Sun, 13 Jun 2004, [ISO-8859-1] Gon?alo Rodrigues wrote:
> 
> > >I have been looking at
> > >
> > >http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205
> > >
> > >which has an example of using a Null() object.
> > >
> > >Unfortunately, I'm having a problem wrapping my brain around the
> > >usefulness of this object compared to just using None.
> > >
> > >Does anyone have some further examples of the 'superiority' of this
> > >approach over *None*.
> >
> > Imagine a null object as a kind of sink: it responds to all messages
> > without barfing.
> 
> 
> Hi Tim,
> 
> If you're familiar with the use of a "sentinel" to simplify a program's
> logic, then you can think of a "Null Object" as an OOP analogy to a
> sentinel.
> 
> 
> I like using concrete examples, and one of the nice examples where
> sentinels come in really handy is something like a Minesweeper-like game
> program.  Here's a quick link to one:
> 
> http://www.programming-challenges.com/pg.php?page=downloadproblem&probid=110102&format=html
> 
> In the game of Minesweeper, we might want to report the number of mines
> adjacent to a space.  For example, if we have:
> 
> *...
> ....
> .*..
> ....
> 
> we'd like to get back something like:
> 
> *100
> 2210
> 1*10
> 1110
> 
> 
> If we represent a minefield as a list of rows, like this:
> 
> minefield = ['*...',
>              '....',
>              '.*..',
>              '....']
> 
> and if we wanted to get the number of mines at an
> arbitrary row and col, we might write something like:
> 
> ###
> def countMines(row, col):
>     """Returns the number of mines around position(row, col)."""
>     count = 0
>     for i in [-1, 0, 1]:
>         for j in [-1, 0, 1]:
>             if minefield[row + i][col +j] == '*':
>                 count += 1
>     return count
> ###
> 
> 
> The only problem is that this code doesn't work.  *grin*
> 
> ###
> >>> countMines(3, 3)
> Traceback (most recent call last):
>   File "<stdin>", line 1, in ?
>   File "<stdin>", line 6, in countMines
> IndexError: string index out of range
> ###
> 
> The problem is that there's this special logic around the edges of the
> board, so our beautiful countMines() function malfunctions around the
> board edges.
> 
> 
> One way to fix countMines() is to add special-case logic into countMines()
> for counting mines around the edges of the board, maybe something like:
> 
> ###
> if not (0 <= row + i < 4): continue
> ###
> 
> 
> But there's another way of fixing the problem, and it involves using
> sentinels.  In effect, we take something like:
> 
> *...
> ....
> .*..
> ....
> 
> 
> and wrap a sentinel border of '=' characters around the whole game board:
> 
> ======
> =*...=
> =....=
> =.*..=
> =....=
> ======
> 
> With this sentinel border, the code doesn't have to worry about the edge
> of the board so much.  If I ask for the number of mines on the
> bottom-right corner --- countMines(4, 4) --- then we're perfectly ok,
> since we don't go off the board.  Does this make sense?
> 
> 
> Null Objects perform a similar function: they are "sentinels" that allow
> us to simplify our main code, so that we don't have to worry so much about
> the special case of handling a reference to None.
> 
> 
> Hope this helps!
> 
> 
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> http://mail.python.org/mailman/listinfo/tutor

-- 
Tim Johnson <tim at johnsons-web.com>
      http://www.alaska-internet-solutions.com



More information about the Tutor mailing list