Structural Patterns
18 min
Structural patterns are about composing objects together — wrapping one object inside another to change or extend how it's used, without touching its original code. The Adapter pattern wraps an object whose interface doesn't match what you need, translating calls to match:
class OldRectangle: # a class you can't (or don't want to) modify
def __init__(self, w, h):
self.w, self.h = w, h
def compute_area(self): # wrong method name for your codebase
return self.w * self.h
class RectangleAdapter:
def __init__(self, old_rect):
self.old_rect = old_rect
def area(self): # the interface your code actually expects
return self.old_rect.compute_area()
This is exactly how you'd integrate a third-party library (whose method names you can't change) into code written against your own project's interface conventions.
The Decorator pattern (a different thing from the @decorator
syntax from the Python course, though conceptually related) wraps an
object to ADD behavior, while keeping the SAME interface — callers can't
tell the difference between the original object and the decorated one:
class LoggingShape:
def __init__(self, shape):
self.shape = shape
self.call_count = 0
def area(self):
self.call_count += 1 # added behavior
return self.shape.area() # delegates to the wrapped object
logged = LoggingShape(Rectangle(3, 4))
print(logged.area()) # 12 -- works exactly like a normal shape
print(logged.call_count) # 1 -- but now you can track usage
Unlike Adapter (which changes the interface), Decorator preserves it
exactly — LoggingShape still has .area(), just like Rectangle does,
so it can be used anywhere a Shape is expected.
Given `OldRectangle` (with `compute_area()`, not `area()`) below, write `RectangleAdapter.area(self)` (the Adapter pattern): make the adapter expose the expected `.area()` interface by calling the wrapped object's `.compute_area()`.
Write `LoggingShape.area(self)` (the Decorator pattern): wrap another shape, incrementing `self.call_count` every time `area()` is called, then return the wrapped shape's actual area.
What's the key difference between the Adapter and Decorator patterns?
You can use the Adapter pattern to translate a mismatched interface, and the Decorator pattern to add behavior around an object while keeping its interface identical.