Definition
- Adapter – converts one interface into another so incompatible types can work together, used to integrate legacy or third-party code.
- Decorator – adds behaviour to an object at runtime by wrapping it in another object that implements the same interface.
- Composite – treats individual objects and groups of objects uniformly through a tree structure.
- Proxy – stands in for another object to control access, adding lazy creation, security, or caching.
- Facade – provides one simple front to a complex subsystem, hiding interactions.
- Bridge – separates an abstraction from its implementation so both can vary independently.
- Flyweight – shares common immutable state across many objects to save memory.
At National Bank of India: an Adapter is the converter that lets the new app talk to the old mainframe. A Decorator is adding gift wrapping to a parcel layer by layer without changing the parcel. A Composite is a folder of accounts that contains both single accounts and sub-folders, all totalled the same way. A Proxy is the security guard who checks your ID before letting you reach the manager. A Facade is the single help desk that hides all the back departments. A Bridge is a remote control whose buttons stay the same while the device behind it can change.
Understanding the Concepts
What is the Adapter pattern?
Adapter makes two incompatible interfaces work together by wrapping one in a class that exposes the interface the other expects. Rajesh uses it to reuse existing or third-party code without rewriting it.
package com.bank.adapter;
import java.math.BigDecimal;
// Target interface the bank's code expects
public interface LedgerPort {
void post(String description, BigDecimal amount);
}
// Legacy system with incompatible interface
public class LegacyLedger {
public void writeEntry(String desc, double amt) {
System.out.println("Legacy: " + desc + " = " + amt);
}
}
// Adapter bridges the gap
public class LegacyLedgerAdapter implements LedgerPort {
private final LegacyLedger legacy;
public LegacyLedgerAdapter(LegacyLedger legacy) {
this.legacy = legacy;
}
@Override
public void post(String description, BigDecimal amount) {
legacy.writeEntry(description, amount.doubleValue());
}
}
What is the Decorator pattern?
Decorator adds responsibilities to an object dynamically by wrapping it in another object that implements the same interface. Each decorator adds one concern and delegates the rest.
package com.bank.decorator;
public interface Notifier {
void send(String message);
}
public class SmsNotifier implements Notifier {
@Override
public void send(String message) {
System.out.println("SMS: " + message);
}
}
public class LoggingNotifier implements Notifier {
private final Notifier wrapped;
public LoggingNotifier(Notifier wrapped) { this.wrapped = wrapped; }
@Override
public void send(String message) {
System.out.println("LOG: " + message);
wrapped.send(message);
}
}
public class EncryptingNotifier implements Notifier {
private final Notifier wrapped;
public EncryptingNotifier(Notifier wrapped) { this.wrapped = wrapped; }
@Override
public void send(String message) {
String encrypted = "ENC[" + message + "]";
wrapped.send(encrypted);
}
}
// Stack decorators in any order
Notifier notifier = new LoggingNotifier(
new EncryptingNotifier(new SmsNotifier()));
notifier.send("Transfer of INR 50000 completed");
LOG: Transfer of INR 50000 completed
SMS: ENC[Transfer of INR 50000 completed]
What is the Composite pattern?
Composite lets you treat a single object and a group of objects the same way by building a tree where both leaves and branches implement one interface.
package com.bank.composite;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public interface PortfolioComponent {
BigDecimal marketValue();
}
public class Holding implements PortfolioComponent {
private final String name;
private final BigDecimal value;
public Holding(String name, BigDecimal value) {
this.name = name; this.value = value;
}
@Override
public BigDecimal marketValue() { return value; }
}
public class Portfolio implements PortfolioComponent {
private final List<PortfolioComponent> children = new ArrayList<>();
public void add(PortfolioComponent c) { children.add(c); }
@Override
public BigDecimal marketValue() {
return children.stream()
.map(PortfolioComponent::marketValue)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
What is the Proxy pattern?
Proxy provides a stand-in that controls access to a real object, adding security checks, lazy loading, or caching behind the same interface.
package com.bank.proxy;
import com.bank.service.AccountService;
import java.math.BigDecimal;
public class SecureAccountProxy implements AccountService {
private final AccountService real;
private final String role;
public SecureAccountProxy(AccountService real, String role) {
this.real = real;
this.role = role;
}
@Override
public void withdraw(String accountId, BigDecimal amount) {
if (!"TELLER".equals(role)) {
throw new SecurityException("Unauthorised withdrawal");
}
real.withdraw(accountId, amount);
}
}
This is exactly how Spring's @Transactional proxy wraps a bean.
What is the Facade pattern?
Facade offers one simple interface over a complicated subsystem. A TransferFacade hides validation, fee calculation, ledger posting, and notification behind a single call.
package com.bank.facade;
import java.math.BigDecimal;
public class TransferFacade {
private final ValidationService validator;
private final FeeService fees;
private final LedgerService ledger;
private final NotificationService notifier;
public TransferFacade(ValidationService v, FeeService f,
LedgerService l, NotificationService n) {
this.validator = v; this.fees = f;
this.ledger = l; this.notifier = n;
}
public void transfer(String fromId, String toId, BigDecimal amount) {
validator.validate(fromId, toId, amount);
BigDecimal fee = fees.calculate(amount);
ledger.post(fromId, toId, amount, fee);
notifier.notifyCustomer(fromId, "Transfer of INR " + amount + " sent");
}
}
What is the Bridge pattern?
Bridge separates an abstraction from its implementation so they can vary independently, avoiding a class explosion. For example, an Account abstraction holds a reference to an InterestEngine implementation.
package com.bank.bridge;
import java.math.BigDecimal;
public interface InterestEngine {
BigDecimal calculate(BigDecimal principal, int days);
}
public class SimpleInterestEngine implements InterestEngine {
@Override
public BigDecimal calculate(BigDecimal principal, int days) {
BigDecimal rate = new BigDecimal("0.04");
return principal.multiply(rate)
.multiply(BigDecimal.valueOf(days))
.divide(BigDecimal.valueOf(365), 2, java.math.RoundingMode.HALF_UP);
}
}
public abstract class Account {
protected final InterestEngine engine;
protected Account(InterestEngine engine) { this.engine = engine; }
public abstract BigDecimal calculateInterest(int days);
}
public class SavingsAccount extends Account {
private final BigDecimal balance;
public SavingsAccount(BigDecimal balance, InterestEngine engine) {
super(engine); this.balance = balance;
}
@Override
public BigDecimal calculateInterest(int days) {
return engine.calculate(balance, days);
}
}
What is the Flyweight pattern?
Flyweight shares common immutable state across many objects. For example, thousands of trades can share a single interned Currency object rather than each holding a duplicate.
package com.bank.flyweight;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CurrencyFactory {
private static final Map<String, Currency> pool = new ConcurrentHashMap<>();
public static Currency of(String code) {
return pool.computeIfAbsent(code, Currency::new);
}
}
public class Currency {
private final String code;
Currency(String code) { this.code = code; }
public String getCode() { return code; }
}
Currency inr1 = CurrencyFactory.of("INR");
Currency inr2 = CurrencyFactory.of("INR");
System.out.println(inr1 == inr2); // true: same shared instance
Java's Integer.valueOf cache is a Flyweight.
How do Adapter, Decorator, and Proxy differ?
All three wrap an object, but their intent differs. Naming the intent is what shows real understanding.
| Pattern | Intent | Interface change? |
|---|---|---|
| Adapter | Convert one interface to another | Yes, changes the interface |
| Decorator | Add behaviour | No, same interface |
| Proxy | Control access | No, same interface |
// Adapter: changes interface
LedgerPort port = new LegacyLedgerAdapter(legacyLedger);
// Decorator: adds behaviour, same interface
Notifier n = new LoggingNotifier(new SmsNotifier());
// Proxy: controls access, same interface
AccountService s = new SecureAccountProxy(realService, "TELLER");
What should a senior engineer know?
A senior engineer recognises these patterns in real frameworks: Java I/O streams are Decorator, Spring @Transactional is Proxy, SLF4J bindings are Adapter, and Integer.valueOf is Flyweight. The key is naming the intent, not just the structure.
In the account and notification subsystem at National Bank of India, Pankaj uses an Adapter to bridge the old ledger, Decorators to layer logging and encryption on notifications, a Composite to total Rajesh's nested portfolio, a Proxy to enforce authorisation on balance reads, and a Facade to expose a single clean transfer call. Being able to point at each one in real code is what turns textbook knowledge into practical engineering.
Visual Overview
┌──────────────────────────────────────────────────┐ │ Seven Structural Patterns │ │ │ │ Adapter Decorator Composite │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │Change │ │ Add │ │ Tree │ │ │ │iface │ │behav.│ │leaves│ │ │ └──────┘ └──────┘ │+nodes│ │ │ └──────┘ │ │ Proxy Facade Bridge │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │Control│ │Simple│ │Split │ │ │ │access │ │front │ │abs + │ │ │ └──────┘ └──────┘ │impl │ │ │ └──────┘ │ │ Flyweight │ │ ┌──────┐ │ │ │Share │ │ │ │state │ │ │ └──────┘ │ └──────────────────────────────────────────────────┘
Adapter vs Decorator vs Proxy ───────────────────────────── Adapter: Client ──→ [Adapter] ──→ Adaptee (different interface) Decorator: Client ──→ [Deco] ──→ [Deco] ──→ Real (same interface, adds) Proxy: Client ──→ [Proxy] ──→ Real (same interface, controls)
Key Takeaway
Structural patterns are about how you compose classes and objects into larger structures while keeping them flexible. Adapter converts one interface into another so incompatible types work together, which is how Rajesh wraps a legacy ledger behind a clean LedgerPort. Decorator adds behaviour at runtime by wrapping an object that shares the same interface, so logging, encryption, and SMS can be stacked in any order without subclass explosion. Composite treats single objects and groups uniformly through a tree, which is how a portfolio totals its value whether a node is a holding or a sub-portfolio.
Proxy provides a stand-in that controls access to the real object, adding security checks or lazy loading, which is exactly how Spring's transactional proxy wraps a bean. Facade gives one simple entry point over a complicated subsystem, so a client calls a single transfer method instead of orchestrating validation, fees, ledger posting, and notification. Bridge separates abstraction from implementation so the two can vary independently, letting any account type combine with any interest engine. And Flyweight shares common immutable state across many objects to save memory, which is how millions of trades reuse one interned currency object.
The critical distinction is that Adapter, Decorator, and Proxy all wrap an object, but their intent is different. Adapter changes the interface, Decorator adds behaviour while keeping the same interface, and Proxy controls access while keeping the same interface. Naming the intent, not just the structure, is what shows real understanding.
In the account and notification subsystem at National Bank of India, these appear together naturally: an Adapter bridges the old ledger, Decorators layer logging and encryption on notifications, a Composite totals Rajesh's nested portfolio, a Proxy enforces authorisation on balance reads, and a Facade exposes a single clean transfer call.
Remember one sentence: structural patterns compose objects into larger structures, and the difference between Adapter, Decorator, and Proxy is intent: convert, add, or control.
---