RSS

Blackboard Pattern

Blackboard Architectural Pattern

The Blackboard Architectural Pattern is useful for problems for which no deterministic solution strategies are known. In Blackboard several specialised sub-systems assemble their knowledge to build a possibly partial or approximate solution.

blackboard pattern uml

See the paper by D. Deugo, M. Weiss and E. Kendall.

Andy’s Presentation

Blackboard Pattern Slides by Andy

Example Code

This is a pattern that is difficult to do an example for because you would normally have to set up a large infrastructure of classes etc. I refused to be put off and presented a simple blackboard pattern implementation in python, during my talk to the Melbourne Patterns Group. Please forgive its limitations but it should at least give you an idea of what this pattern is about.

Note that you can flick a switch in the code below and have a small GUI pop up in Swing via jython.

"""
  Blackboard system
  by Andy Bulka
  Prepared for the Melbourne Patterns group - August 2004
"""
import random
True = 1
False = 0

print "Welcome to my blackboard system"

class Blackboard:
    def __init__(self):
        self.experts = []
        self.commonState = {}

    def AddExpert(self, expert):
        self.experts.append(expert)

class Controller:
    def __init__(self, blackboard):
        self.blackboard = blackboard
        # init blackboard
        self.blackboard.commonState['answer'] = 0
        self.blackboard.commonState['answerCorrectness'] = 0

    def Loop(self):
        while self.blackboard.commonState['answerCorrectness'] < 90:
            candidates = []
            for expert in self.blackboard.experts:
                eagerness = expert.CanContribute()
                if eagerness:
                    candidates.append((eagerness,expert))  # append a tuple

            candidates.sort()    # move winning tuple to the end of the list
            winningExpert = candidates[-1][1]  # -1 means the last item in list.
            winningExpert.execAction()

        return self.blackboard.commonState['answer']

    def Loop_OLD(self):
        while self.blackboard.commonState['answerCorrectness'] < 90:
            for expert in self.blackboard.experts:
                if expert.CanContribute():
                    expert.execAction()
        return self.blackboard.commonState['answer']


class AbstractExpert:
    def __init__(self, blackboard, outtext):
        self.blackboard = blackboard
        self.outtext = outtext

    def CanContribute(self):
        raise 'not implemented'

    def execAction(self):
        raise 'not implemented'

class SmartAss(AbstractExpert):

    def CanContribute(self):
        return random.randint(1,20)

    def execAction(self):
        self.blackboard.commonState['answer'] += random.randint(1,20)
        print '.',
        self.outtext.text += '.'

class WiseMan(AbstractExpert):

    def CanContribute(self):
        if self.blackboard.commonState['answer'] > 200:
            return random.randint(1,20)
        else:
            return False

    def execAction(self):
        self.blackboard.commonState['answer'] += 1
        self.blackboard.commonState['answerCorrectness'] += 5
        print '*',
        self.outtext.text += '*'

WANT_JAVA_GUI = False

if WANT_JAVA_GUI:
    from javax.swing import JFrame, JLabel, JButton, JTextField

    class JHutton(JButton):
        pass

    class GUI:
        def __init__(self):
            f = JFrame()
            f.show()
            f.size = 200,200
            f.title = "Blackboard Jungle"

            f.contentPane.add(JLabel("Expert1"))

            self.txt1 = JTextField(30)
            f.contentPane.add(self.txt1)

            f.contentPane.add(JLabel("Expert2"))

            self.txt2 = JTextField(30)
            f.contentPane.add(self.txt2)

            button = JHutton("Think")
            f.contentPane.add(button)
            button.actionPerformed = self.onClick

            from java.awt import FlowLayout
            f.contentPane.layout = FlowLayout()

            f.pack()
            f.visible = 1
            self.f = f

            blackboard = Blackboard()
            blackboard.AddExpert( SmartAss(blackboard, self.txt1) )
            blackboard.AddExpert( WiseMan(blackboard, self.txt2) )
            self.c = Controller(blackboard)

        def onClick(self, event):
            result = self.c.Loop()
            print
            print result
            print 'done'

    gui = GUI()
else:
    # Pure text
    class DummyTextWidget:
        def __init__(self):
            self.text = ''

    import sys
    blackboard = Blackboard()
    blackboard.AddExpert( SmartAss(blackboard, DummyTextWidget()))
    blackboard.AddExpert( WiseMan(blackboard, DummyTextWidget()))
    c = Controller(blackboard)
    result = c.Loop()
    print
    print result
    print 'done'

Output

Welcome to my blackboard system
. . . . . . . . . . . . . . . . . . . * . * * * * * . * . * . * . . * * * . * . . * * . . * . . * . *
371
done

Example - UML Layout

Another real world example of this pattern being used in is my open source Python UML tool where I used this pattern to evaluate numerous attempts at laying out a UML diagram, scoring the result and then choosing the best layout.

Here is the source code of that Python module on GitHub.