Structural Design Patterns: Implementation and Practical Use Cases

Adapter Patern

The Adapter pattern enables incompatible interfaces to collaborate by converting one interface into another expected by the client. It acts as a bridge between legacy and modern components without altering their internal logic.

JavaScript Example

Suppose we need to integrate a new audio player with an existing system that only supports MP3. We wrap the legacy system with an adapter that normalizes the interface.

class AudioRenderer {
  play(format, path) {
    throw new Error('Interface not implemented');
  }
}

class LegacyCodec {
  playMp3(filename) {
    console.log(`Playing MP3: ${filename}`);
  }
}

class AudioBridge extends AudioRenderer {
  constructor() {
    super();
    this.codec = new LegacyCodec();
  }

  play(format, path) {
    const handlers = {
      mp3: () => this.codec.playMp3(path),
      wav: () => this.transcodeAndPlay(path, 'WAV'),
      ogg: () => this.transcodeAndPlay(path, 'OGG')
    };

    (handlers[format] || (() => console.log('Unsupported format')))();
  }

  transcodeAndPlay(file, format) {
    console.log(`Converting ${format} to playable stream: ${file}`);
  }
}

const player = new AudioBridge();
player.play('mp3', 'track.mp3');
player.play('wav', 'sound.wav');

PHP Example

Integrating an old payment processor with a modern gateway using adapter pattern.

interface PaymentProcessor {
    public function charge(float $amount): void;
}

class LegacyGateway {
    public function executeTransaction(float $amount): void {
        echo "Legacy system processed: \${$amount}\n";
    }
}

class PaymentAdapter implements PaymentProcessor {
    private $legacy;

    public function __construct(LegacyGateway $legacy) {
        $this->legacy = $legacy;
    }

    public function charge(float $amount): void {
        $this->legacy->executeTransaction($amount);
    }
}

$legacy = new LegacyGateway();
$adapter = new PaymentAdapter($legacy);
$adapter->charge(89.99);

Use Cases

  • Integrating third-party libraries with incompatible APIs
  • Migrating legacy systems without rewriting dependent code
  • Enabling backward compatibility during API evolution

Bridge Pattern

The Bridge pattern decouples an abstraction from its implementation so both can vary independent. This is ideal when multiple dimensions of variation exist.

JavaScript Example

Rendering shapes across different output mediums (screen, printer).

class Renderer {
  renderCircle(radius) { throw new Error('Abstract method'); }
}

class ScreenRenderer extends Renderer {
  renderCircle(radius) {
    console.log(`Rasterizing circle: radius ${radius} on screen`);
  }
}

class VectorRenderer extends Renderer {
  renderCircle(radius) {
    console.log(`Generating SVG circle: radius ${radius}`);
  }
}

class Shape {
  constructor(renderer) {
    this.renderer = renderer;
  }
}

class Circle extends Shape {
  constructor(renderer, radius) {
    super(renderer);
    this.radius = radius;
  }

  draw() {
    this.renderer.renderCircle(this.radius);
  }
}

const screen = new ScreenRenderer();
const vector = new VectorRenderer();

new Circle(screen, 10).draw();
new Circle(vector, 10).draw();

PHP Example

Separating notification types from delivery channels.

interface DeliveryChannel {
    public function transmit(string $message): void;
}

class EmailChannel implements DeliveryChannel {
    public function transmit(string $message): void {
        echo "Email: {$message}\n";
    }
}

class SmsChannel implements DeliveryChannel {
    public function transmit(string $message): void {
        echo "SMS: {$message}\n";
    }
}

abstract class Notification {
    protected $channel;

    public function __construct(DeliveryChannel $channel) {
        $this->channel = $channel;
    }

    abstract public function send(string $content): void;
}

class UrgentAlert extends Notification {
    public function send(string $content): void {
        $this->channel->transmit("[URGENT] {$content}");
    }
}

$alert = new UrgentAlert(new SmsChannel());
$alert->send("System failure detected");

Use Cases

  • Multiple platforms with shared logic (e.g., UI components across OS)
  • Combinatorial explosion prevention (e.g., shape × rendering back end)
  • Runtime switching of implementations (e.g., database drivers)

Composite Pattern

Composite allows treating individual objects and compositions uniformly, forming tree-like structures for hierarchical data.

JavaScript Example

File system with directories and files.

class Node {
  constructor(name) {
    this.name = name;
  }

  addChild(node) { throw new Error('Not implemented'); }
  removeChild(node) { throw new Error('Not implemented'); }
  traverse(depth = 0) { throw new Error('Not implemented'); }
}

class File extends Node {
  traverse(depth) {
    console.log('  '.repeat(depth) + '📄 ' + this.name);
  }
}

class Folder extends Node {
  constructor(name) {
    super(name);
    this.children = [];
  }

  addChild(node) {
    this.children.push(node);
  }

  removeChild(node) {
    this.children = this.children.filter(n => n !== node);
  }

  traverse(depth = 0) {
    console.log('  '.repeat(depth) + '📁 ' + this.name);
    this.children.forEach(child => child.traverse(depth + 1));
  }
}

const root = new Folder('home');
const docs = new Folder('documents');
const photo = new File('vacation.jpg');

docs.addChild(new File('report.pdf'));
docs.addChild(photo);
root.addChild(docs);

root.traverse();

PHP Example

interface Component {
    public function operation(): void;
    public function add(Component $child): void;
    public function remove(Component $child): void;
}

class TextFile implements Component {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function operation(): void {
        echo "Processing file: {$this->name}\n";
    }

    public function add(Component $child): void {
        throw new Exception("Files cannot contain children");
    }

    public function remove(Component $child): void {
        throw new Exception("Files cannot contain children");
    }
}

class Directory implements Component {
    private $name;
    private $items = [];

    public function __construct($name) {
        $this->name = $name;
    }

    public function add(Component $child): void {
        $this->items[] = $child;
    }

    public function remove(Component $child): void {
        $this->items = array_filter($this->items, fn($item) => $item !== $child);
    }

    public function operation(): void {
        echo "Entering directory: {$this->name}\n";
        foreach ($this->items as $item) {
            $item->operation();
        }
    }
}

$root = new Directory('project');
$root->add(new TextFile('README.md'));
$root->add(new TextFile('config.json'));
$root->operation();

Use Cases

  • File systems, UI component trees
  • Organization hierarchies (departments → teams → employees)
  • XML/JSON parsing where nested elements are uniform

Decorator Pattern

Decorator dynamically adds responsibilities to objects without subclassing. It promotes composition over inheritance.

JavaScript Example

Enhancing text formatting with layered decorators.

class Text {
  constructor(content) {
    this.content = content;
  }

  render() {
    return this.content;
  }
}

class TextDecorator extends Text {
  constructor(text) {
    super(text.content);
    this.wrapped = text;
  }

  render() {
    return this.wrapped.render();
  }
}

class BoldDecorator extends TextDecorator {
  render() {
    return `<b>${super.render()}</b>`;
  }
}

class ItalicDecorator extends TextDecorator {
  render() {
    return `<i>${super.render()}</i>`;
  }
}

const base = new Text("Hello");
const boldItalic = new ItalicDecorator(new BoldDecorator(base));
console.log(boldItalic.render()); // <b><i>Hello</i></b>

PHP Example

Building customizable coffee orders with optional add-ons.

abstract class Beverage {
    protected $description = 'Unknown';

    public function getDescription() { return $this->description; }
    abstract public function cost(): float;
}

class DarkRoast extends Beverage {
    public function __construct() {
        $this->description = 'Dark Roast Coffee';
    }

    public function cost(): float { return 2.20; }
}

abstract class CondimentDecorator extends Beverage {
    public abstract function getDescription();
}

class WhippedCream extends CondimentDecorator {
    private $beverage;

    public function __construct(Beverage $beverage) {
        $this->beverage = $beverage;
    }

    public function getDescription(): string {
        return $this->beverage->getDescription() . ', Whipped Cream';
    }

    public function cost(): float {
        return $this->beverage->cost() + 0.50;
    }
}

$coffee = new DarkRoast();
$coffee = new WhippedCream($coffee);
echo $coffee->getDescription() . ": $" . number_format($coffee->cost(), 2);

Use Cases

  • Dynamic UI enhancements (borders, shadows, animations)
  • Request logging, authentication, or caching layers
  • Game item enhancements (weapons with enchantments)

Facade Pattern

Facade provides a simplified interface to a complex subsystem, hiding intricate details behind a single entry point.

JavaScript Example

Unified interface for home automation systems.

class Lighting {
  turnOn() { console.log('Lights ON'); }
  turnOff() { console.log('Lights OFF'); }
}

class Thermostat {
  setTemperature(temp) { console.log(`Temp set to ${temp}°C`); }
}

class Security {
  arm() { console.log('Security armed'); }
  disarm() { console.log('Security disarmed'); }
}

class HomeAutomation {
  constructor() {
    this.lights = new Lighting();
    this.thermo = new Thermostat();
    this.sec = new Security();
  }

  morningRitual() {
    this.lights.turnOn();
    this.thermo.setTemperature(22);
    this.sec.disarm();
  }

  nightRitual() {
    this.lights.turnOff();
    this.thermo.setTemperature(18);
    this.sec.arm();
  }
}

const system = new HomeAutomation();
system.morningRitual();

PHP Example

Consolidated checkout process in e-commerce.

class InventoryChecker {
    public function verify($productId) {
        echo "Stock verified for ID: {$productId}\n";
    }
}

class OrderProcessor {
    public function create($productId, $qty) {
        echo "Order created: {$qty} units of {$productId}\n";
    }
}

class PaymentGateway {
    public function charge($amount) {
        echo "Charged: \${$amount}\n";
    }
}

class CheckoutFacade {
    private $inventory;
    private $order;
    private $payment;

    public function __construct() {
        $this->inventory = new InventoryChecker();
        $this->order = new OrderProcessor();
        $this->payment = new PaymentGateway();
    }

    public function completePurchase($productId, $qty, $amount) {
        $this->inventory->verify($productId);
        $this->order->create($productId, $qty);
        $this->payment->charge($amount);
    }
}

$checkout = new CheckoutFacade();
$checkout->completePurchase('SKU-789', 2, 199.99);

Use Cases

  • API gateways for microservices
  • Legacy system wrappers
  • SDKs that abstract underlying complexity

Flyweight Pattern

Flyweight reduces memory usage by sharing common data among similar objects. Ideal for large-scale object creation with repetitive properties.

JavaScript Example

Sharing font and color properties in a text editor.

class Glyph {
  constructor(font, color) {
    this.font = font;
    this.color = color;
  }

  render(char, x, y) {
    console.log(`Rendering '${char}' at (${x},${y}) in ${this.color} ${this.font}`);
  }
}

class GlyphFactory {
  static cache = new Map();

  static getGlyph(font, color) {
    const key = `${font}:${color}`;
    if (!GlyphFactory.cache.has(key)) {
      GlyphFactory.cache.set(key, new Glyph(font, color));
    }
    return GlyphFactory.cache.get(key);
  }
}

function renderText(text, font, color) {
  const glyph = GlyphFactory.getGlyph(font, color);
  for (let i = 0; i < text.length; i++) {
    glyph.render(text[i], i * 10, 50);
  }
}

renderText("Hello", "Arial", "blue");
renderText("World", "Arial", "blue"); // Reuses same glyph

PHP Example

Game with thousands of trees using shared models.

interface TreeFlyweight {
    public function render(int $x, int $y): void;
}

class PineTree implements TreeFlyweight {
    public function render(int $x, int $y): void {
        echo "Rendering pine tree at ($x, $y)\n";
    }
}

class TreeFactory {
    private $pool = [];

    public function getTree(string $type): TreeFlyweight {
        if (!isset($this->pool[$type])) {
            $this->pool[$type] = match ($type) {
                'pine' => new PineTree(),
                default => throw new InvalidArgumentException("Unknown tree type")
            };
        }
        return $this->pool[$type];
    }
}

$factory = new TreeFactory();
$tree1 = $factory->getTree('pine');
$tree2 = $factory->getTree('pine');

$tree1->render(100, 200);
$tree2->render(110, 210); // Same object reused

Use Cases

  • Graphics engines with repeated sprites
  • Text editors with recurring character styles
  • Database connection pooling

Proxy Pattern

Proxy controls access to an object, enabling lazy initialization, access control, or logging.

JavaScript Example

Lazy-loaded image proxy for performance optimization.

class ImageResource {
  constructor(url) {
    this.url = url;
    this.loaded = false;
  }

  load() {
    console.log(`Loading image: ${this.url}`);
    this.loaded = true;
  }

  display() {
    if (!this.loaded) this.load();
    return `<img alt="loaded" src="${this.url}"></img>`;
  }
}

class ImageProxy {
  constructor(url) {
    this.url = url;
    this.realImage = null;
  }

  display() {
    if (!this.realImage) {
      this.realImage = new ImageResource(this.url);
    }
    return this.realImage.display();
  }
}

const proxy = new ImageProxy('https://example.com/large.jpg');
document.body.innerHTML = proxy.display(); // Loads only on first access

PHP Example

Deferred database query execution.

class DataFetcher {
    private $data;

    public function fetch(): array {
        if (!$this->data) {
            echo "Querying database...\n";
            $this->data = range(1, 100000);
        }
        return $this->data;
    }
}

class DataProxy {
    private $fetcher;
    private $cachedData;

    public function __construct() {
        $this->fetcher = new DataFetcher();
    }

    public function getData(): array {
        if ($this->cachedData === null) {
            $this->cachedData = $this->fetcher->fetch();
        }
        return $this->cachedData;
    }
}

$proxy = new DataProxy();
echo "Accessing data...\n";
$proxy->getData(); // Triggers fetch
$proxy->getData(); // Uses cached result

Use Cases

  • Lazy loading of heavy resources
  • Access control (authentication proxies)
  • Remote service stubs (e.g., SOAP/REST wrappers)
  • Logging and monitoring wrappers

Summary: Structural Patterns Comparison

Pattern Primary Goal Key Benefit
Adapter Interface compatibility Integrate without modification
Bridge Separate abstraction from implementation Independent evolution
Composite Treat objects and groups uniformly Recursive structure handling
Decorator Dynamic behavior extension Flexible composition
Facade Simplify complex subsystems Reduced coupling
Flyweight Minimize memory via sharing High-scale efficiency
Proxy Control object access Deferred execution & security

Tags: adapter-pattern bridge-pattern composite-pattern decorator-pattern facade-pattern

Posted on Sat, 22 Aug 2026 16:06:03 +0000 by amitkrathi