Python design pattern: class that returns different objects depending on parameters

Viewed 404

This question concerns design patterns in Python and is addressed to the software designers.

I have several classes inherited from the same abstract class (all they have a similar interface, but the member functions have very different implementations). I want to make a class (or something else) that combines (wraps) all of them. By combining I mean a pattern that creates and returns an object a certain class depending on the input parameters. The most trivial solution is a Python function:

def fabric_function(arg):
    if isinstance(arg, float):
        return Class1(arg)
    if isinstance(arg, str):
        return Class2(arg)

Is there a better pattern available to do it? I would really prefer it to be a class that returns either object of Class1 or Class2 depending on parameters. I want the user to make an instance of a class Class that operates as an instance of the Class1 or Class2 depending on input parameters so the user does not have to make a decision on which of them to use and the output from type(obj) should be Class in all cases. In this case, I could modify the member function __new__ responsible for the object making process, but I am not sure it is a clean way to do it. Maybe I should use multiple inheritance and decide in the constructor which parent class to inherit for real?

2 Answers

I suppose that Class1 and Class2 are related somehow (eg, "Square" and "Circle" are both "Figures" ). What you are describing is an Abstract Factory.

You need a class exposing a Create() method, accepting the bare minimum context that would help you find out what's the concrete implementation to instantiate.

I did something similar once using a dictionary. I requires the init functions of your derived classes to have the same signatures in order to work.

Use a dictionary with some immutable type of your choice as key and put the classnames as values:

TYPE_MAP = {typeA: Class1,
            typeB: Class2,
            typeC: Class3}

def fabric_function(arg):
    return TYPE_MAP[type(arg)](arg)

I'm not exactly sure how pythonic/clean you consider this but it helped me greatly in avoiding this if isinstance... chain you posted above. Also it is very easy to extend as you only have to adjust the dictionary and not the factory function.

Related