Facade Pattern

designpatterns
Published

June 2, 2024

The Facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem. It hides the details of the subsystem behind a single, easy-to-use interface. This makes it easier to interact with the subsystem and reduces the complexity for clients. Think of it as a friendly receptionist for a large and confusing office – you just tell the receptionist what you need, and they handle the details of routing your request to the appropriate department.

When to Use the Facade Pattern

You should consider using the Facade pattern when:

Example: A Simplified File System

Let’s imagine we have a complex file system with different components responsible for file creation, reading, writing, and deletion. Each component might have its own API. Using a Facade, we can simplify the interaction:

// Subsystem classes (complex components)
class FileCreator {
  createFile(filename, content) {
    console.log(`Creating file: ${filename} with content: ${content}`);
    // ...complex file creation logic...
  }
}

class FileReader {
  readFile(filename) {
    console.log(`Reading file: ${filename}`);
    // ...complex file reading logic...
    return "File content";
  }
}

class FileWriter {
  writeFile(filename, content) {
    console.log(`Writing to file: ${filename} with content: ${content}`);
    // ...complex file writing logic...
  }
}

class FileDeleter {
  deleteFile(filename) {
    console.log(`Deleting file: ${filename}`);
    // ...complex file deletion logic...
  }
}

// Facade class
class FileManager {
  constructor() {
    this.creator = new FileCreator();
    this.reader = new FileReader();
    this.writer = new FileWriter();
    this.deleter = new FileDeleter();
  }

  create(filename, content) {
    this.creator.createFile(filename, content);
  }

  read(filename) {
    return this.reader.readFile(filename);
  }

  write(filename, content) {
    this.writer.writeFile(filename, content);
  }

  delete(filename) {
    this.deleter.deleteFile(filename);
  }
}

// Client code
const fileManager = new FileManager();
fileManager.create("myFile.txt", "Hello, world!");
const content = fileManager.read("myFile.txt");
console.log(content); // Output: File content
fileManager.write("myFile.txt", "Updated content!");
fileManager.delete("myFile.txt");

In this example, FileManager acts as the Facade. The client code interacts only with FileManager, unaware of the underlying complexity of the FileCreator, FileReader, FileWriter, and FileDeleter classes.

Benefits of Using the Facade Pattern

Example: A More Abstract Facade

We can make the Facade even more abstract by using a single method to handle different file operations:

class FileManager {
  // ... (FileCreator, FileReader, FileWriter, FileDeleter remain the same) ...

  execute(command, filename, content) {
    switch (command) {
      case 'create':
        this.creator.createFile(filename, content);
        break;
      case 'read':
        return this.reader.readFile(filename);
      case 'write':
        this.writer.writeFile(filename, content);
        break;
      case 'delete':
        this.deleter.deleteFile(filename);
        break;
      default:
        throw new Error('Invalid command');
    }
  }
}

const fileManager = new FileManager();
fileManager.execute('create', 'myFile2.txt', 'New file content!');
console.log(fileManager.execute('read', 'myFile2.txt'));
fileManager.execute('delete', 'myFile2.txt');

This approach further simplifies the client interaction, making it even cleaner and easier to understand. This exemplifies the flexibility and power of the Facade Pattern.