Low-level design2 min

Facade Pattern

Type: Structural Pattern
Relevance: High. You use this constantly when wrapping complex 3rd-party libraries.

The Facade pattern provides a simplified, higher-level interface to a complex subsystem of classes, libraries, or frameworks.

Real-Life Analogy

Think of ordering food at a Drive-Thru. Behind the counter is a complex subsystem: the grill cook, the fry cook, the cashier, the inventory system, the receipt printer. As a customer, you don't want to coordinate all of these people. You just speak to the Drive-Thru Operator (The Facade). You say, "I want a burger meal." The operator talks to the grill, the fryer, and the register, and simply hands you your food.

The Problem

Imagine your code needs to work with a sophisticated video conversion library. To convert an MP4 to an AVI, you have to initialize a video codec, initialize an audio codec, configure the bitrates, link a byte buffer, and start a processing thread. If you put all this initialization logic directly into your UI button click handler, your code becomes incredibly tightly coupled to the library and impossible to read.

How to Implement Facade

Create a new class that encapsulates all the complex initialization and coordination logic, exposing only a few simple methods (e.g., convertVideo(filename, format)).

Example in Code

typescript
// The Complex Subsystem
class VideoFile { constructor(public name: string) {} }
class OggCompressionCodec { type = "ogg"; }
class MPEG4CompressionCodec { type = "mp4"; }
class CodecFactory {
  extract(file: VideoFile) { console.log(`Extracting ${file.name}`); }
}
class BitrateReader {
  static read(filename: string, codec: any) { console.log("Reading bitrate..."); return "buffer"; }
  static convert(buffer: string, codec: any) { console.log("Converting..."); return "new_buffer"; }
}
class AudioMixer {
  fix(result: string) { console.log("Fixing audio..."); return "final_video_file"; }
}

// -------------------------------------------------------------------
// The Facade!
// Hides all the terrifying complexity behind a single simple method.
// -------------------------------------------------------------------
class VideoConverterFacade {
  public convertVideo(filename: string, format: string): string {
    console.log(`\nFacade: Starting conversion of ${filename} to ${format}...`);
    
    const file = new VideoFile(filename);
    const sourceCodec = new CodecFactory().extract(file);
    let destinationCodec;

    if (format === "mp4") {
      destinationCodec = new MPEG4CompressionCodec();
    } else {
      destinationCodec = new OggCompressionCodec();
    }

    const buffer = BitrateReader.read(filename, sourceCodec);
    const result = BitrateReader.convert(buffer, destinationCodec);
    const finalFile = new AudioMixer().fix(result);

    console.log("Facade: Conversion complete!");
    return finalFile;
  }
}

// Client Code
// The client only needs to know about the Facade!
const converter = new VideoConverterFacade();
const mp4Video = converter.convertVideo("funny_cats.ogg", "mp4");

Class Diagram

Why is it Important?

  • Isolates Complexity: The client code isn't polluted with dozens of lines of configuration for a 3rd-party library.
  • Loose Coupling: If the 3rd-party library updates its API and breaks everything, you only have to fix the Facade class, rather than hunting down library calls scattered across your entire application.