Card 06/ 08
A Bridge Built Before There Is a Second Dimension
One implementor and an interface in front of it is a layer that varies nothing.
A bridge pays for itself only when both sides really vary. Built for one abstraction and one implementor, in case a second appears, it is an extra interface and an extra reference that every reader must follow and nothing uses. This card shows the speculative version beside the direct one, and the signal that a bridge is due.
A bridge with one sideone implementor, in case
class ReportStorage(ABC):
@abstractmethod
def write(self, name, data): ...
class S3ReportStorage(ReportStorage): ...
class Report:
def __init__(self, storage: ReportStorage):
self._storage = storageIt takes two classes and an interface to save one kind of report to one place.
The direct classuntil a second storage exists
class Report:
def __init__(self, bucket):
self._bucket = bucket
def save(self, name, data):
self._bucket.put_object(name, data)It is one class, read in one place, and easy to split when a second storage arrives.
The speculative version works, and it costs every reader an extra hop to find what save does. The class count says why it saves nothing: with one variant on each side, M times N and M plus N are one and two, and the bridge is the larger.
The signal is the second variant on the implementation side, a test that wants storage in memory or a customer that needs a second cloud. That is the point to extract the interface, and it is a small, mechanical change when the class was kept direct. An interface added for a test double alone is a reasonable seam, but it becomes a bridge only when the abstraction side varies too.
← → move through sections and cards