Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory method for objects - best practice?

This is a question regarding the best practice for creating an instance of a class or type from different forms of the same data using python. Is it better to use a class method or is it better to use a separate function altogether? Let's say I have a class used to describe the size of a document. (Note: This is simply an example. I want to know the best way to create an instance of the class not the best way to describe the size of a document.)

class Size(object):     """     Utility object used to describe the size of a document.     """      BYTE = 8     KILO = 1024      def __init__(self, bits):         self._bits = bits      @property     def bits(self):         return float(self._bits)      @property     def bytes(self):         return self.bits / self.BYTE      @property     def kilobits(self):         return self.bits / self.KILO      @property     def kilobytes(self):         return self.bytes / self.KILO      @property     def megabits(self):         return self.kilobits / self.KILO      @property     def megabytes(self):         return self.kilobytes / self.KILO 

My __init__ method takes a size value represented in bits (bits and only bits and I want to keep it that way) but lets say I have a size value in bytes and I want to create an instance of my class. Is it better to use a class method or is it better to use a separate function altogether?

class Size(object):     """     Utility object used to describe the size of a document.     """      BYTE = 8     KILO = 1024      @classmethod     def from_bytes(cls, bytes):         bits = bytes * cls.BYTE         return cls(bits) 

OR

def create_instance_from_bytes(bytes):     bits = bytes * Size.BYTE     return Size(bits) 

This may not seem like an issue and perhaps both examples are valid but I think about it every time I need to implement something like this. For a long time I have preferred the class method approach because I like the organisational benefits of tying the class and the factory method together. Also, using a class method preserves the ability to create instances of any subclasses so it's more object orientated. On the other hand, a friend once said "When in doubt, do what the standard library does" and I am yet to find an example of this in the standard library.

like image 490
Yani Avatar asked Feb 21 '13 00:02

Yani


People also ask

When should you use a factory method?

The Factory Method pattern is generally used in the following situations: A class cannot anticipate the type of objects it needs to create beforehand. A class requires its subclasses to specify the objects it creates. You want to localize the logic to instantiate a complex object.

What is a Factory Method Pattern good for?

Factory Method Pattern allows the sub-classes to choose the type of objects to create. It promotes the loose-coupling by eliminating the need to bind application-specific classes into the code.

Which are the three types of factory method?

the abstract factory pattern,the static factory method, the simple factory (also called factory).


1 Answers

First, most of the time you think you need something like this, you don't; it's a sign that you're trying to treat Python like Java, and the solution is to step back and ask why you need a factory.

Often, the simplest thing to do is to just have a constructor with defaulted/optional/keyword arguments. Even cases that you'd never write that way in Java—even cases where overloaded constructors would feel wrong in C++ or ObjC—may look perfectly natural in Python. For example, size = Size(bytes=20), or size = Size(20, Size.BYTES) look reasonable. For that matter, a Bytes(20) class that inherits from Size and adds absolutely nothing but an __init__ overload looks reasonable. And these are trivial to define:

def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None): 

Or:

BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object() def __init__(self, count, unit=Size.BITS): 

But, sometimes you do need factory functions. So, what do you do then? Well, there are two kinds of things that are often lumped together into "factories".

A @classmethod is the idiomatic way to do an "alternate constructor"—there are examples all over the stdlib—itertools.chain.from_iterable, datetime.datetime.fromordinal, etc.

A function is the idiomatic way to do an "I don't care what the actual class is" factory. Look at, e.g., the built-in open function. Do you know what it returns in 3.3? Do you care? Nope. That's why it's a function, not io.TextIOWrapper.open or whatever.

Your given example seems like a perfectly legitimate use case, and fits pretty clearly into the "alternate constructor" bin (if it doesn't fit into the "constructor with extra arguments" bin).

like image 60
abarnert Avatar answered Oct 16 '22 09:10

abarnert