Thu 11 October 2018

Iterator Design Pattern

Before I started reading the gang of 4 ["Go4"], I was convinced I would not need an iterator. After all, in python, they are already implemented:

x = ['a', 'b', 'c']

for i in x:
    print(i)

In the frame of python lists as aggregate objects, the intent of an iterator is satisfied.

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representations.

Displaying notes

Here I explain, how the iterator became my favourite design pattern.

I have written about representation of lists before, and I don't think this will be my last time. When implementing the drop-downs in foolscap to allow a user to see sections contained in their note, I ran into an issue of complexity where I had blocks of conditionals dictating what should be displayed to a user. I wanted the user to have an indication that a note contains sections, and for them to be able to toggle a drop-down to see each section. Similar to:

 (+) | circleci  |
     | docker    |

Where my "circleci" note would contain sections and the "docker" note does not. Then expanding:

 <-> | circleci  |
  └─ | -workflow |
     | docker    |

This is where I realised the abstraction power of an iterator, And I could hide the collapsed sections behind an "if" conditional in an iterator.

class Menu:

    def __init__(self, items):
        self.items = items

    def visible(self):
        """Yield the next appropriate item for display."""
        for item in self.items:
            yield item.title
            if hasattr(item, 'expand') and item.expand:
                for sub_item in item.sub_items:
                    yield sub_item.title

Supporting further traversal policies in my Menu class is straightforward, and drawing becomes absurdly simplified:

def draw(menu):
    """Draw all viewable menu items."""
    for item in menu.visible():
        draw_item(item)

After this I thought of a more complex aggregate structure that I could traverse, like a tree depth first.

Realising the abstraction strength of the iterator, one finds its definition of intent far more compelling.

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representations.