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 and FilteredLogger. Instead of subclasses for specific destinations, "adapt" the destination Socket/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 a Logger/FilteredLogger.
  • Solution 2 : Bridge Pattern. Split the class behaviour into:

    1. An abstraction object, seen by the caller.
    2. An implementation object, that is wrapped inside it.

    Here, we can decide (arbitrarily) that FilteredLogger is the outer abstraction class. We can then have FileHandler, 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 classes FileLogger, SocketLogger, SyslogLogger and filter class LogFilter (which has an output logger as an attribute).

Resources

Emacs 29.4 (Org mode 9.6.15)