freude
freude

Reputation: 3832

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

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?

Upvotes: 2

Views: 1204

Answers (3)

Albert
Albert

Reputation: 626

This is easy in Python. Use __new__!

In python, when an instance is made, aka the class is called, the special method __new__ is called. You can override it, like any method/operator in python.

Typically, one does so in the base class (aka interface). So, your suggestion is right.

The implementation is simple: mostly the code you provided in this “factory”. Although you might add a few details like checks to prevent strange behavior when using unexpected/unwanted arguments or subclasses.

Remember, returning None is fine. Actually anything is allowed (but unwise, when you’re not what to disturb your users)

Upvotes: 0

FloWil
FloWil

Reputation: 442

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.

Upvotes: 1

David Guida
David Guida

Reputation: 1165

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.

Upvotes: 1

Related Questions