In Python, with iterators, the Visitor design pattern is useless. And a strongly-ingrained habit. Which I'm trying to break.
Here's a common Visitor approach:
class Visitor:
def __init__( self ): ...
def visit( self, some_target_thing ): ...
def all_done( self ): ...
v = Visitor()
for thing in some_iterator():
v.visit(thing)
v.all_done()
If we refactor the for statement into the Visitor, then it's just a Command or something.
Here's the refactored Iterating Visitor:
class Command:
def __init__( self ): ...
def process_all( self, iterable ):
for thing in iterable:
self.visit( thing )
def visit( self, thing ): ...
def all_done( self ): ...
c=Command()
c.process_all( some_iterator() )
c.all_done()
Possible Objection
The one possible objection is this: "What if our data structure is so hellishly complex that we can't reduce it to a simple iterator?"
That's perfectly silly. Any hyper-complex algorithm to walk any hyper-complex data structure, no matter how hyper complex, can always be recast into a generator function which uses yield to iterate over the objects.
Better Design
Once we start down this road, we can generally simplify processing into a kind of Command that looks something like this.
class Command:
def __init__( self ): ...
def run( self ):
for thing in self.iterable:
....
c= Command()
c.iterable= some_iterator()
c.run()
I find that this interface is somewhat easier to deal with when composing large commands from individual small commands. It follows a Create-Configure-Run pattern that seems to work out well. I just wish I would start with this rather than start with a Visitor, refactor, and end up with this.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.