Feb 14, 2013

Composite Design Pattern in Python

The structure of the composite design pattern, how it will look in Python and a succinct solution for example a console menu, that contain menu items, each of which could be a menu.

Design patterns have as their explicit supporters, as well as numerous critics. Supporters see them as almost a silver bullet that elegantly solves existing problems of the complexity in projects written in OOP languages, like Smalltalk, C++, Java. Critics attribute this to the limitations of the OOP languages themselves and prove their elegant solutions in functional programming languages.

But is it worth to use Design Patterns in Python that has the properties of both groups?

Below I give an example of using Composite pattern in Python, but i am almost sure that the same functionality can be implemented in a functional style. If You know how, it will be interesting to see your examples.

In classical literature you can see the structure of the pattern like:


But why use interfaces in python? We will use the simpler structure:

class Leaf:
    def do_this(self):
        pass

class Composite(Leaf):
    def do_this(self):
        pass
 
    def add_item(self):
        pass
It looks too easy. To be able to feel any benefit here is an example of the console menu development:

class MenuItem():

    def __init__(self, name, action):
        self.name = name
        self.action = action

    def __str__(self):
        return self.name

class Menu(MenuItem):

    def __init__(self, name):
        MenuItem.__init__(self, name, self.show_menu)
        self.items={}

    def add_item(self, item):
        self.items[len(self.items) + 1] = item

    def show_menu(self):
        choice = -1
        while choice:
            cls()
            print(self)
            choice = read_number()
            if choice in self.items:
                self.items[choice].action()

    def __str__(self):
        res_str = self.name + ':\n0: Exit'
        for i in self.items.keys():
            res_str += str.format('\n{}: {}', i, self.items[i].name)
        return res_str

# rest part of the code is auxiliary
def cls():
    print(80*'\n')

def read_number(text = ''):
    INP='>>'
    text += '\n' + INP if text else INP
    str = ''
    try:
        str = input(text)
        return int(str)
    except ValueError:
        press_enter("Error value: '{}'! Enter please the number".format(str))
        return -1

def press_enter(str = ''):
    str += '\nPress Enter'
    input(str)

if __name__ == '__main__':
    main_menu = Menu('Main')
    main_menu.add_item(MenuItem('A', lambda : press_enter('A')))
    main_menu.add_item(MenuItem('B', lambda : press_enter('B')))
    sub_menu = Menu('Submenu')
    sub_menu.add_item(MenuItem('Sub D', lambda : press_enter('D')))
    sub_menu.add_item(MenuItem('Sub E', lambda : press_enter('E')))
    main_menu.add_item(sub_menu)
    main_menu.show_menu()

You can also download full source: compositeDP.py

2 comments:

  1. Wanted to advertise on your blog - where can I write in? Or alternately can you write in to my email - sanket[dot]nadhani[at]gmail?

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete