samedi 15 juillet 2017

Design pattern - How to add callbacks?

Problem:

Programming model that uses observer pattern, where an object(subject) maintains bunch of observers and send different types of notifications to bunch of observers


Solution:

#subject.py

"""
An object called subject,
1) Maintains the list of dependents, called observers 
2) Automatically notifies observers, about different type of events
"""
import random
import threading
import time
from eventloop import eventLoop

import pdb

def generateEventHelper(eventLoop):
    for i in range(10):
        time.sleep(random.random())
        eventLoop.sendEvent('data', i+random.random())
        print('Event sent')
    eventLoop.sendEvent('done')

def launchThreadHelper(eventLoop):
    t = threading.Thread(target=generateEventHelper, args=(eventLoop, ))
    t.start()

# action code for a specific event
acc = 0
def addToAccAction(e, eventLoop): # Not sure, how to avoid 2nd arg
    global acc
    global numOfEvents
    acc += e[1]
    print(acc)

# action code for done event
doneEvents = 0
def shutDownAction(e, eventLoop):
    global doneEvents
    doneEvents += 1
    if doneEvents == 4: # After 4 done events
        print('Event loop is shutting down')
        eventLoop.shutDown()

def subject():
    # Add observer, in this case
    eventLoop.addEventListener( lambda x: x[0] == 'data',
                                addToAccAction)

    eventLoop.addEventListener( lambda x: x[0] == 'done',
                                shutDownAction)

    # Now generate events - can be data or state or whatever
    launchThreadHelper(eventLoop)
    launchThreadHelper(eventLoop)
    launchThreadHelper(eventLoop)
    launchThreadHelper(eventLoop)

    # Notifying those events to relevant observers
    eventLoop.runLater(2, lambda: print('2 seconds are up***********************'))
    eventLoop.runForever()

# pdb.set_trace()
if __name__ == '__main__':
    subject()


#eventloop.py

"""
EventLoop help subject,
1) to maintain the observers
2) to notify observers
3) to process different types of events
"""
import queue
import time

class EventLoop():
    def __init__(self):
        self.events = queue.Queue()
        self.observers = []
        self.timedCallbacks = []
        self.pleaseShutDown = False
        self.startTime = None

    def sendEvent(self, *args):
        self.events.put(args)
        print('Got an event') # Debugging

    def runForever(self):
        while True:
            e = self.events.get()
            self.processEvent(e)
            if self.pleaseShutDown:
                break
            now = time.time()
            toRun = [(t,cb) for (t, cb) in self.timedCallbacks if t < now]
            for t, cb in toRun:
                if t < now:
                    cb()
                    self.timedCallbacks.remove((t,cb))

    def runLater(self, when, callback):
        self.timedCallbacks.append((time.time() + when, callback))

    def processEvent(self, e):
        for pred, action in self.observers:
            if pred(e):
                action(e, self)

    def shutDown(self):
        self.pleaseShutDown = True

    def addEventListener(self, predicate, action):
        self.observers.append((predicate, action))

# Singleton
eventLoop = EventLoop() # To maintain observers and notify them


Question:

All event types & observers(ex: addToAccAction) are scattered in subject.py, Please suggest a design pattern to add new event types and observers

Aucun commentaire:

Enregistrer un commentaire