Card 03/ 05
A Creational Pattern Where a Constructor Was Enough
The class has one implementation, three fields and one caller, and now it has a factory too.
A creational pattern is worth its classes only when something about creation varies, and the most common misuse is a pattern added where nothing does. This card shows a factory around a class that did not need one, and the three questions that would have said so before it was written.
With a factorytwo extra types for one class
class AddressFactory(ABC):
@abstractmethod
def create(self, street, city, postcode): ...
class DefaultAddressFactory(AddressFactory):
def create(self, street, city, postcode):
return Address(street, city, postcode)
address = DefaultAddressFactory().create("1 High St", "Leeds", "LS1 1AA")A reader must open three classes to learn that an address is made from three strings.
With a constructorthe class says how it is made
@dataclass(frozen=True)
class Address:
street: str
city: str
postcode: str
address = Address("1 High St", "Leeds", "LS1 1AA")The call names the class it creates, and every field is required and visible.
Three questions decide it. Is there more than one class the caller might need, now and not in a guess about next year? Must several objects be created to match? Are there optional parts, or so many fields that a call is hard to read? When every answer is no, the constructor is the design, and a pattern adds indirection that every reader pays for while no requirement uses it.
The extra classes are only part of the cost. A factory interface invites a second implementation that never comes, a test needs a fake factory to make a value, and the class's own constructor, the one place a reader looks first, no longer shows how the object is meant to be made. Adding the pattern later, when a second address format appears, is a small change: the constructor calls become calls to one factory method.
← → move through sections and cards