Introduction
Using a fluent interface can make your code easier to read. This pattern allows you to connect method calls, as each method returns the object or context. When implemented well, it can create a kind of specialized language for a specific domain.
Let’s see how this works in Python
Implementation in Python
Because in our Python implementation the methods in the class need to return an object of that class we need to import this first:
from typing import Self
In this example we will create a simple Address
type:
class Address:
_street: str = None
_number: int = None
_city: str = None
_country: str = None
def street(self, street: str) -> Self:
self._street = street
return self
def number(self, number: int) -> Self:
self._number = number
return self
def city(self, city: str) -> Self:
self._city = city
return self
def country(self, country: str) -> Self:
self._country = country
return self
def __str__(self) -> str:
return f"{self._street} {self._number} in {self._city},{self._country}"
A few notes:
- There is no explicit constructor
- Each method, apart from the
__str__()
method, returnSelf
which is an object of the classAddress
in our case.This is done so method calls can be chained, as we will see later. - The
__str__()
is a utility to make printing theAddress
easier.
Testing time
Now we can test our setup:
if __name__ == "__main__":
address = Address()
address.street("Middle road").number(50).city("Arcadia").country("Utopia")
print(address)
A short description:
- We create an empty address.
- Then, through method chaining, we set its fields
- And we print out the address, so we know the fields have been set correctly.
Conclusion
The fluent interface enhances code readability and flexibility. However, it may not always be the best choice. Consider trade-offs, as while it improves readability in some cases, it could lead to unwieldy chains of method calls, increasing complexity.