Factory pattern implementation in Python

Chris Mellon arkanes at gmail.com
Mon Dec 4 12:27:45 EST 2006


On 4 Dec 2006 08:39:17 -0800, googlegroups at romulo.e4ward.com
<googlegroups at romulo.e4ward.com> wrote:
> Hi,
>
> I need to parse a binary file produced by an embedded system, whose
> content consists in a set of events laid-out like this:
>
> <event 1> <data 1> <event 2> <data 2> ... <event n> <data n>
>
> Every "event" is a single byte in size, and it indicates how long is
> the associated "data". Thus, to parse all events in the file, I need to
> take it like a stream and read one event at a time, consuming bytes
> according to the event value, and jumping to the next event, until an
> EOF is reached.
>
> Since there are dozens of almost completely heterogeneous events and
> each one of them may imply different actions on the program parsing the
> file, I thought it would be convenient to have one class encapsulating
> the logic for every event. The parser would then sit in a loop,
> creating objects of different classes and calling a method (say
> "execute"). That method (different in every class) is responsible for
> consuming the bytes associated with the event.
>
> Hence, as the class the parser needs to instantiate in each iteration
> is not known in advance, a factory should be implemented. Somehow the
> factory should know how to map an event to a class. I don't know of the
> best way I should do that in Python. I made an attempt along the
> following lines:
>
> 1. Create a base class for the events;
> 2. For every descendant class declare (in the class body) a public
> attribute "eventNum" and assign it the value of the event it will be
> responsible for;
> 3. At runtime, the factory constructor scans the event class hierarchy
> and builds a dictionary mapping "eventNum"'s to classes.
>
> A draft of the implementation follows:
>
> #################################
>
> ##### <events.py module> #####
>
> class EvtBase:
>   def __init__(self, file):
>     self.file = file
>
>   def execute(self):
>     pass
>
> class Evt1(EvtBase):
>   eventNum = 1
>   def execute(self):
>     ...
>
> class Evt2(EvtBase):
>   eventNum = 2
>   def execute(self):
>     ...
>
> ...
>
> class EvtN(EvtBase):
>   eventNum = N
>   def execute(self):
>     ...
>
>
> ##### <factory.py module> #####
>
> import inspect
> import events
>
> class Factory:
>   def __isValidEventClass(self, obj):
>     if inspect.isclass(obj) and obj != events.EvtBase and \
>         events.EvtBase in inspect.getmro(obj):
>       for m in inspect.getmembers(obj):
>         if m[0] == 'eventNum':
>           return True
>     return False
>
>   def __init__(self):
>     self.__eventDict = {}
>     for m in inspect.getmembers(events, self.__isValidEventClass):
>       cls = m[1]
>       self.__eventDict.update({cls.eventNum: cls})
>
>   def parseEvents(self, file):
>     while not file.eof():
>       ev = file.read(1)
>       self.__eventDict[ev](file).execute()
>
> #################################
>
> I'm using the inspect module to find the event classes. One drawback of
> this approach is the need to keep the event classes in a module
> different from that of the factory, because the getmembers method
> expects an already parsed object or module. (The advantage is keeping
> the event number near the class declaration.) I've already had to make
> the solution generic and I found it was not straightforward to separate
> the common logic while avoiding the need to keep the factory and the
> events in two distinct modules.
>
> Is there anything better I can do? I don't have enough experience with
> Python, then I don't know whether it offers a more obvious way to
> address my problem.
>

I'd have the classes register themselves rather than trying to find
them. This removes the need to have a common base class (preserves
duck typing) and lets everything communicate via the factory module.

#in module Factory.py

EventMap = {}

#in module events.py

import Factory
class EventHandler:
    Factory.EventMap[1] = EventHandler

#in module parser.py

import Factory

handler = Factory.EventMap[event]()
handler.handleEvent(data)


There's probably some way to wrap the registration up in a metaclass
so it's handled implicitly, but I prefer the explicit approach.


> Thanks in advance.
>
> --
> Romulo A. Ceccon
> 'romulo%s\x40yahoo.com.br' % 'ceccon'
>
> --
> http://mail.python.org/mailman/listinfo/python-list
>



More information about the Python-list mailing list