Sometimes I make the joke that design patterns are all about getting rid of if-else statements from your code. The null object pattern is an example of a pattern that does just that - check out the code at the bottom for details.
A Null Object provides a surrogate for another object that shares the same interface, but does nothing.
This pattern was originally written up by Bobby Wolf, in Pattern Languages of Program Design 3.
Null Object Pattern
Yes, I have used this pattern a few times in my work. You have to be a little bit careful about managing the swapping of null obect for the real thing. If bits of your code are pointing to the null object then you can't easily swap in the real thing. Better to put the null object / real object behind a proxy so that nobody knows what you are doing!
Here is a python example. The so called API class is a real class with real functionality in it. We decide that we want to log calls to our API class (note the name of this class could be anything, and its got nothing to do with api's).
from time import asctime, localtime
class AbstractObject: pass # pretend python has abstract classes
class RealLogging:
def Log(self, msg):
print 'Logged at', asctime(localtime()), msg
# Proxy / wrapper around either null or real logger.
class Logger:
def __init__(self):
self.logger = RealLogging()
def Log(self, msg):
if self.logger:
self.logger.Log(msg)
def On(self):
self.logger = RealLogging()
def Off(self):
self.logger = None
Logger = Logger()
# Usage:
class API:
def doA(self):
if Logger.logger:
Logger.Log('Am calling A')
print 'A done.'
def doB(self):
if Logger.logger:
Logger.Log('Am calling B')
print 'B done.'
o = API()
o.doA()
o.doB()
Logger.Off()
o.doA()
o.doB()
# Null Object Pattern
class AbstractLogging:
def Log(self, msg): pass
from time import asctime, localtime
class RealLogging(AbstractObject):
def Log(self, msg):
print 'Logged at', asctime(localtime()), msg
class NullLogging(AbstractObject):
def Log(self, msg):
return
# Proxy / wrapper around either null or real logger.
class Logger:
def __init__(self):
self.On()
def Log(self, msg):
self.logger.Log(msg)
def On(self):
self.logger = RealLogging()
def Off(self):
self.logger = NullLogging()
Logger = Logger()
# Usage:
class API:
def doA(self):
Logger.Log('Am calling A')
print 'A done.'
def doB(self):
Logger.Log('Am calling B')
print 'B done.'
o = API()
o.doA()
o.doB()
Logger.Off()
o.doA()
o.doB()
Without logging:
A done.
B done.
With logging:
Logged at Fri Jan 23 17:28:01 2009 Am calling A
A done.
Logged at Fri Jan 23 17:28:01 2009 Am calling B
B done.
Notice no more "if statements" in the client code (API class).