user3827132
user3827132

Reputation: 149

How to design a class in python (oo pattern help)

I'm implementing an "engine" class (described below in detail) and I'm not sure what kind of object to use. I've tried doing a little reading on OO patterns but I'm still not sure. I think my question is language agnostic, but fwiw I'm using Python.

I want to make a class that gets some initialization (e.g. a database connection and some other configuration) and then it can be called repeatedly to process bits of info. For the bits of info, I've broken up my logic into a bunch of methods, but now I have a huge calling signature for each method because I need to pass all kinds of things into each one.

The calling code will look something like this:

db = get_a_db_connection()
my_engine = Engine(db, config)
while True:
  my_info = get_info_from_somewhere()
  my_engine.process_info(my_info)

And the actual Engine class as I have it looks something like this:

class Engine(object):

  def __init__(self, db, config):
    self.db = db
    # Also store the config - it's a little more complicated than
    # this but I am abstracting away details that don't seem needed
    self.config = config

  def process_info(self, info):
    foo = self.method1(info)
    bar = self.method2(info, foo)
    baz = self.method3(info, bar)
    qux = self.method4(info, foo, bar, baz) 
    bla = self.method5(info, bar, baz, qux)

  def method1(self, info):
    # Do something and return intermediate info
    return some_transformation_on_info

  # Definitions for method2 - method5 (and more) follow

  def method2(self, info, foo):
    ...

  <snip>

It seems like it'd be nice to be able to store those intermediate things as attributes so I don't need to pass them as parameters every time. But it doesn't seem appropriate to store them as attributes since they are specific to a piece of info and not the class as a whole.

Is this a case where I use the factory pattern to create an intermediate object that actually does the processing of info?

Upvotes: 2

Views: 127

Answers (3)

pillmuncher
pillmuncher

Reputation: 10162

You could pass an environment around:

class Engine(object):

  def __init__(self, db, config):
    self.db = db
    self.config = config

  def process_info(self, info):
    env = {'info': info}
    self.method1(env)
    self.method2(env)
    self.method3(env)
    self.method4(env)
    self.method5(env)

  def method1(self, env):
    env['foo'] = some_transformation_on_info(env['info'])

  def method2(self, env):
    env['bar'] = something_from(env['foo'])

  def method3(self, env):
    env['baz'] = my_func(env['bar'])

  def method4(self, env):
    env['qux'] = your_func(env['foo'], env['bar'], env['baz'])

  def method5(self, env):
    env['bla'] = your_func(env['bar'], env['baz'], env['qux'])

That way, process_info() doesn't need to know what's going on in all the methods it calls.

If you don't like the dictionary subscription syntax, you can do this:

class Environment(object):
    pass

class Engine(object):

  def __init__(self, db, config):
    self.db = db
    self.config = config

  def process_info(self, info):
    env = Environment()
    env.info = info
    self.method1(env)
    self.method2(env)
    self.method3(env)
    self.method4(env)
    self.method5(env)

  def method1(self, env):
    env.foo = some_transformation_on_info(env.info)

  def method2(self, env):
    env.bar = something_from(env.foo)

  def method3(self, env):
    env.baz = my_func(env.bar)

  def method4(self, env):
    env.qux = your_func(env.foo, env.bar, env.baz)

  def method5(self, env):
    env.bla = your_func(env.bar, env.baz, env.qux)

Upvotes: 0

glglgl
glglgl

Reputation: 91017

class EngineInfoProcessor(object):

    def __init__(self, engine, info):
        self.engine = engine
        self.info = info

    def process(self):
        foo = self.engine.method1(info)
        self.foo = foo # if you want
        yield foo # if you want
        bar = self.method2(info, foo)
        self.bar = bar # ...
        yield bar # ...
        baz = self.method3(info, bar)
        self.baz = baz
        yield baz
        qux = self.method4(info, foo, bar, baz) 
        self.qux = qux
        yield qux
        bla = self.method5(info, bar, baz, qux)
        self.bla = bla
        yield bla

might be what you want.

Upvotes: -1

Emanuele Paolini
Emanuele Paolini

Reputation: 10162

It much depends on the logic of the parameters. But you can consider defining a binding object which has two attributes: an engine and a info. Then you move all these function on the binding object.

Upvotes: 3

Related Questions