Bridge decouples an abstraction from its implementation so both can evolve independently; Flyweight shares common state across many objects to cut memory footprint — via a text-editor glyph example.
Published September 22, 2026
The problem Bridge solves: an abstraction (say, Shape) and its implementation (say, RenderingEngine) both need to vary independently — new shapes get added, and new rendering backends get added, and neither should force changes in the other.
If Shape subclasses each hard-code their rendering approach (VectorCircle, RasterCircle, VectorSquare, RasterSquare...), every new shape times every new rendering backend multiplies the class count — the same explosion problem Decorator solves for behavior, but here it's for two independently-varying dimensions.
interface RenderingEngine { // the "implementation" side
void renderCircle(double x, double y, double radius);
}
class VectorRenderer implements RenderingEngine {
public void renderCircle(double x, double y, double radius) { /* draw as vector paths */ }
}
class RasterRenderer implements RenderingEngine {
public void renderCircle(double x, double y, double radius) { /* draw as pixels */ }
}
abstract class Shape { // the "abstraction" side
protected final RenderingEngine renderer; // the bridge — composed, not inherited
Shape(RenderingEngine renderer) { this.renderer = renderer; }
abstract void draw();
}
class Circle extends Shape {
private double x, y, radius;
Circle(RenderingEngine renderer, double x, double y, double radius) {
super(renderer);
this.x = x; this.y = y; this.radius = radius;
}
void draw() { renderer.renderCircle(x, y, radius); } // delegates across the bridge
}
Shape vectorCircle = new Circle(new VectorRenderer(), 0, 0, 5);
Shape rasterCircle = new Circle(new RasterRenderer(), 0, 0, 5);
Adding a new shape (Square) means one new Shape subclass, reusable with any existing RenderingEngine. Adding a new backend (GpuRenderer) means one new RenderingEngine implementation, usable by any existing shape. The two hierarchies vary completely independently — this is the same "hold a reference instead of extending" idea as Dependency Inversion and Strategy, applied specifically to keep two dimensions of variation from multiplying against each other.
Flyweight's premise: when you need many objects that share most of their state, factor the shared part out into one reused object, and keep only the truly per-instance state separate.
A document with 100,000 characters, each naively represented as its own object holding a font, size, color, and the character itself, wastes enormous memory — most of that font/size/color state is identical across thousands of characters.
// Intrinsic state (shared, immutable) — the Flyweight
class GlyphStyle {
private final String font;
private final int size;
private final String color;
GlyphStyle(String font, int size, String color) { this.font = font; this.size = size; this.color = color; }
void render(char c, int x, int y) { /* draw c at (x,y) using font/size/color */ }
}
// Flyweight factory — ensures identical styles are actually shared, not duplicated
class GlyphStyleFactory {
private final Map<String, GlyphStyle> cache = new HashMap<>();
GlyphStyle get(String font, int size, String color) {
String key = font + size + color;
return cache.computeIfAbsent(key, k -> new GlyphStyle(font, size, color));
}
}
// Extrinsic state (per-character, NOT shared) stays outside the flyweight
class Character {
char c;
int x, y;
GlyphStyle style; // shared reference, not a copy
}
If a document has 100,000 characters but only 5 distinct (font, size, color) combinations in use, only 5 GlyphStyle objects ever exist — every Character holds a reference to one of those 5 shared instances, rather than duplicating the style data 100,000 times. The position (x, y) and the actual character can't be shared (they're genuinely unique per character) — that's the extrinsic state, kept outside the flyweight and passed in or stored separately.
Q: How is Bridge different from Strategy, since both hold a composed reference instead of inheriting? A: They're structurally similar but differ in intent and timing: Strategy typically swaps one algorithm/behavior at a single point, often chosen per call or per short-lived object; Bridge is about permanently decoupling two entire class hierarchies so each can grow independently over the object's lifetime. In practice the line blurs, and some texts treat Bridge as "Strategy applied at the architectural level."
Q: What's the risk if intrinsic and extrinsic state get mixed up in a Flyweight?
A: If something that should be extrinsic (per-instance, like position) accidentally gets stored inside the shared flyweight object, every "instance" sharing that flyweight would incorrectly share that state too — e.g. all characters using the same GlyphStyle would appear at the same position. Flyweight only works correctly when intrinsic/extrinsic separation is exact.
Q: Doesn't the GlyphStyleFactory's cache risk becoming a memory leak itself? A: Only if styles are added without bound and never removed — for a fixed, small set of styles (fonts/sizes/colors actually used in a document) this isn't a concern, but a system generating unbounded distinct style combinations would need an eviction policy on the cache, same as any other unbounded cache.
Q: Is Flyweight still relevant given how cheap memory is today? A: Less critical for typical business applications, but still very relevant at genuine scale — game engines rendering hundreds of thousands of similar entities, or systems caching large numbers of near-identical configuration objects, still see real, measurable benefit from it.