tags:

views:

67

answers:

3

I'm working on a python class that is being shared between two products. 90% of the functionality applies to both products. For the 10% that's different the code is littered with this kind of thing:

#Start of file
project = 'B'

#Some line of code

if project == 'A':
    import moduleA
elif project == 'B':
    import moduleB

#Many lines of code

if project == 'A':
    print moduleA.doA(2)
elif project == 'B':
    print moduleB.doB(2)

This doesn't seem very elegant or very readable, has anyone encountered this sort of thing before? Are there better ways of doing it?

+3  A: 

No, as this is very poor design. If your module's behavior is influenced by the project in which it is used, then it should accept an object, function, or other callback for the project-specific behavior. My suggestion is to factor out the pieces that are shared and make them into a single module, and for anything that is project-specific, add a dependency on an object that needs to be passed in, and require that the object conform to some interface. Then, wrap your project-specific behaviors in an object that conforms to the required interface and pass it to the common, shared module.

Michael Aaron Safyan
Seems like a good place to be functional.
Xorlev
Thanks for the advice, I'm going to give this a go.
MattyW
A: 

Your shared module should implement a base class with the functionality common to both using modules, then each using module will subclass that base class and add or tweak what's necessary. There are other good approaches (such as the suggestion in @Michael's answer, which is known as the Dependency Injection design patterns), but usually subclassing and overriding is the simplest way to "customize" functionality for different specific needs (it does lead to high and rigid coupling, but that's often quite acceptable as the cost of simplicity).

Alex Martelli
A: 

Another possible approach, if neither of the two answers given so far are practical, is to define the interface at the import level. For instance:

if project == 'A':
  from moduleA import doA as doproject
elif project == 'B':
  from moduleB import doB as doproject

# many lines of code

doproject(2)

This concept (although not this specific problem) is one of the things that "import X as Y" is really good for.

You could also get cleverer with constructing imports based on the project name, making a dictionary keyed by project that had as its value the module to call, and that sort of thing. But I wouldn't go that direction unless I was really sure that refactoring so I could pass in the modules I need wouldn't work.

Vicki Laidler