Python Design Patterns - 2018
Details
Title : Python Design Patterns Author(s): Rhodes, Brandon Link(s) : https://python-patterns.guide/
Rough Notes
Composition over inheritance
"Favour object composition over class inheritance" - Gang of Four.
A problem of inheritance is that classes quickly become too specialized, and subclasses that support all possible combinations arise quickly.
For example, we may have a Logger
class, which is inherited by SocketLogger
and SyslogLogger
. If we add another logger that logs messages based on some filtering criteria, you could create a FilteredLogger
class which contains the filtering criteria. Now, what if someone wants a FilteredSocketLogger
? Or any possible combination. The Gang of Four calls this the "proliferation of classes" and "explosion of subclasses".
We may have a base class Logger
, inherited by SocketLogger
and SyslogLogger
. If we need another logger e.g. filter logs based on some criteria, we can make FilteredLogger
class containing the filtering criteria. Now, we may want a FilteredSocketLogger
, or another combination. This is called the proliferation of classes and explosion of subclasses.
How can we solve this issue?
- Solution 1 : Adapter Pattern.
Keep
Logger
andFilteredLogger
. Instead of subclasses for specific destinations, "adapt" the destinationSocket/Syslog
to the required behaviour by creating a new class (adapter) for each of them, and then pass these objects made from the adapters to aLogger/FilteredLogger
. Solution 2 : Bridge Pattern. Split the class behaviour into:
- An abstraction object, seen by the caller.
- An implementation object, that is wrapped inside it.
Here, we can decide (arbitrarily) that
FilteredLogger
is the outer abstraction class. We can then haveFileHandler
,SocketHandler
,SyslogHandler
as the implementation classes. The abstractions will take an instance of these classes which handle the output, where the actual logging happens.- Solution 3 : Decorator Pattern.
The previous solutions do not allow to stack more than 1 filter at once. If we make the filters and (output) handlers have the same interface so that they all have a
log()
method, we get the decorator pattern. In this case, the handlers are going to be classesFileLogger
,SocketLogger
,SyslogLogger
and filter classLogFilter
(which has an output logger as an attribute).
Resources
- See also github.com/faif/python-patterns.