Facade
The Facade pattern provides a simplified interface to a complex system of classes, objects, and subsystems. It abstracts away the intricacies of the underlying components, offering clients a higher-level, easier-to-use entry point. Essentially, it’s an “entry point” object that encapsulates the interaction with multiple system parts.
This pattern is useful when you want to reduce complexity for clients, promote loose coupling, and offer a more intuitive way to interact with a system. It’s commonly used in scenarios where a system has multiple dependencies or where the client only needs a limited set of features from a larger system. It’s also valuable when migrating to a new subsystem, as the facade can provide compatibility with legacy code while the internal workings change.
Usage
The Facade pattern is commonly found in:
- Complex Libraries/Frameworks: Providing a simple API to interact with a large and intricate codebase.
- System Integration: Abstracting the interaction with different, potentially incompatible systems.
- Layered Architectures: Acting as a gateway to a lower layer from a higher layer, shielding the higher layer from implementation details.
- Build Systems: Managing complex compilation and linking processes with a simplified command.
Examples
-
Apache Camel: Camel uses facades extensively to provide simplified integration patterns for connecting different systems (e.g., databases, message queues, web services). Instead of directly interacting with the underlying transport mechanisms, you define routes using Camel’s DSL, and the facade handles the complexities of the underlying integrations. The
ProducerTemplateandConsumerTemplateclasses provide facades for sending and receiving messages. -
Java Database Connectivity (JDBC): JDBC provides a facade over various database APIs. Developers interact with the database through the
Connection,Statement, andResultSetinterfaces, without needing to know the specific details of how each database vendor implements these functionalities internally. TheDriverManagerclass serves as a facade, simplifying the process of obtaining a database connection. -
Docker SDKs: Docker SDKs for languages like Python and Java offer a facade over the Docker Engine API. You don’t need to learn the intricacies of the Docker Engine’s CLI or REST API; instead, you use high-level functions provided by the SDK to manage containers, images, and networks.
Specimens
15 implementationsThe Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the complexities of the lower-level system. This makes the subsystem easier to use and understand for clients.
The Dart code demonstrates a media player facade, simplifying interactions with components like file system, audio decoder, and video renderer. The MediaPlayer class offers methods like play() and stop() which internally orchestrate the complex operations of these subsystems. This approach encapsulates the complexity and presents a clean, easy-to-use API. Dart’s class-based structure naturally lends itself to the Facade pattern, allowing for clear encapsulation and a well-defined interface.
// Subsystem 1: File System
class FileSystem {
String readFile(String filePath) {
print('Reading file from: $filePath');
return 'File content from $filePath';
}
}
// Subsystem 2: Audio Decoder
class AudioDecoder {
String decode(String data) {
print('Decoding audio data');
return 'Decoded audio: $data';
}
}
// Subsystem 3: Video Renderer
class VideoRenderer {
void renderVideo(String data) {
print('Rendering video: $data');
}
}
// Facade: MediaPlayer
class MediaPlayer {
final FileSystem _fileSystem = FileSystem();
final AudioDecoder _audioDecoder = AudioDecoder();
final VideoRenderer _videoRenderer = VideoRenderer();
void play(String filePath) {
print('Playing media from: $filePath');
String fileContent = _fileSystem.readFile(filePath);
String decodedAudio = _audioDecoder.decode(fileContent);
_videoRenderer.renderVideo(decodedAudio);
print('Media playing...');
}
void stop() {
print('Stopping media...');
// Add stop logic for subsystems if needed
}
}
// Client code
void main() {
final mediaPlayer = MediaPlayer();
mediaPlayer.play('path/to/media.file');
mediaPlayer.stop();
}
The Facade pattern provides a simplified interface to a complex subsystem. It encapsulates multiple interactions within the subsystem into a single, higher-level interface, making it easier for clients to use. This example demonstrates a media player facade, hiding the complexities of audio and video processing. The MediaFacade class offers simple playMovie and playMusic methods, internally coordinating the AudioPlayer, VideoPlayer, and Codec components. This implementation is idiomatic Scala due to its use of classes for encapsulation, clear method signatures, and concise code leveraging Scala’s features.
// Subsystem classes
class AudioPlayer {
def play(audioType: String, fileName: String): Unit = {
println(s"Playing audio file: $fileName (Type: $audioType)")
}
}
class VideoPlayer {
def play(fileName: String): Unit = {
println(s"Playing video file: $fileName")
}
}
class Codec {
def convert(fileType: String, fileName: String): String = {
println(s"Converting file: $fileName (Type: $fileType)")
fileName // Simulate conversion - return the same name
}
}
// Facade class
class MediaFacade {
private val audioPlayer = new AudioPlayer()
private val videoPlayer = new VideoPlayer()
private val codec = new Codec()
def playMovie(fileName: String): Unit = {
val convertedFileName = codec.convert("mp4", fileName)
videoPlayer.play(convertedFileName)
}
def playMusic(fileName: String): Unit = {
val convertedFileName = codec.convert("mp3", fileName)
audioPlayer.play("mp3", convertedFileName)
}
}
// Client code
object Main {
def main(args: Array[String]): Unit = {
val mediaFacade = new MediaFacade()
mediaFacade.playMovie("movie.avi")
mediaFacade.playMusic("song.wav")
}
}
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components. This makes the subsystem easier to use for clients who don’t need to know the details.
The code demonstrates a VideoConversionFacade that encapsulates the complexity of video conversion – including file format checking, codec selection, and actual conversion using separate FileFormatChecker, CodecFactory, and VideoConverter classes. Clients interact only with the Facade, simplifying their code and reducing dependencies on the subsystem’s internal workings. This is idiomatic PHP as it leverages classes to represent components and promotes loose coupling through a dedicated interface, enhancing maintainability and testability.
<?php
/**
* Subsystem classes - these are complex and clients shouldn't interact with them directly.
*/
class FileFormatChecker {
public function isValid(string $file): bool {
// Simulate format checking
return strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'mp4';
}
}
class CodecFactory {
public function createCodec(string $format): Codec {
// Simulate codec creation based on format
if ($format === 'h264') {
return new H264Codec();
}
return new MPEG4Codec();
}
}
interface Codec {
public function encode(string $file): string;
}
class H264Codec implements Codec {
public function encode(string $file): string {
return "Encoding $file to H.264";
}
}
class MPEG4Codec implements Codec {
public function encode(string $file): string {
return "Encoding $file to MPEG4";
}
}
class VideoConverter {
private Codec $codec;
public function __construct(Codec $codec) {
$this->codec = $codec;
}
public function convert(string $file, string $format): string {
return $this->codec->encode($file);
}
}
/**
* The Facade class.
*/
class VideoConversionFacade {
private FileFormatChecker $formatChecker;
private CodecFactory $codecFactory;
private VideoConverter $converter;
public function __construct() {
$this->formatChecker = new FileFormatChecker();
$this->codecFactory = new CodecFactory();
}
public function convertVideo(string $file, string $format): string {
if (!$this->formatChecker->isValid($file)) {
return "Invalid file format.";
}
$codec = $this->codecFactory->createCodec($format);
$this->converter = new VideoConverter($codec);
return $this->converter->convert($file, $format);
}
}
// Client code
$facade = new VideoConversionFacade();
echo $facade->convertVideo("video.mp4", "h264") . "\n";
echo $facade->convertVideo("image.jpg", "h264") . "\n";
?>
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components, making the system easier to use. This example demonstrates a facade for ordering a pizza. The PizzaOrderFacade encapsulates the interactions with the PizzaMaker, PaymentProcessor, and DeliveryService subsystems. The client only needs to interact with the facade, hiding the complex order process. This implementation is idiomatic Ruby due to its use of clear method names, object-oriented structure, and the principle of “duck typing” allowing flexible interactions between the subsystems.
# pizza_maker.rb
class PizzaMaker
def make_pizza(type, size)
puts "Making a #{size} #{type} pizza..."
"#{size} #{type} pizza"
end
end
# payment_processor.rb
class PaymentProcessor
def process_payment(amount, card_details)
puts "Processing payment of $#{amount} with card details..."
true # Simulate successful payment
end
end
# delivery_service.rb
class DeliveryService
def deliver_pizza(address, pizza)
puts "Delivering #{pizza} to #{address}..."
true # Simulate successful delivery
end
end
# pizza_order_facade.rb
class PizzaOrderFacade
def initialize(pizza_maker, payment_processor, delivery_service)
@pizza_maker = pizza_maker
@payment_processor = payment_processor
@delivery_service = delivery_service
end
def order_pizza(type, size, address, card_details)
pizza = @pizza_maker.make_pizza(type, size)
if @payment_processor.process_payment(15.00, card_details)
@delivery_service.deliver_pizza(address, pizza)
puts "Pizza ordered successfully!"
else
puts "Payment failed. Pizza order cancelled."
end
end
end
# client.rb
pizza_maker = PizzaMaker.new
payment_processor = PaymentProcessor.new
delivery_service = DeliveryService.new
pizza_order = PizzaOrderFacade.new(pizza_maker, payment_processor, delivery_service)
pizza_order.order_pizza("Pepperoni", "Large", "123 Main St", "1234-5678-9012-3456")
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components, making the system easier to use. This example demonstrates a Facade for ordering a pizza. The PizzaOrderFacade encapsulates the interactions with the PizzaMaker, PaymentProcessor, and DeliveryService subsystems. The client only needs to interact with the Facade, hiding the complex order process. This implementation is idiomatic Swift by utilizing classes and methods for encapsulation and clear API definition, and leveraging optionals for potential failure states.
// Subsystem classes
class PizzaMaker {
func makePizza(type: String) -> String {
"Making a \(type) pizza..."
}
}
class PaymentProcessor {
func processPayment(amount: Double, cardDetails: String) -> Bool {
print("Processing payment of $\(amount) with card details: \(cardDetails)")
return true // Simulate successful payment
}
}
class DeliveryService {
func deliverPizza(address: String, pizzaDetails: String) -> Bool {
print("Delivering \(pizzaDetails) to \(address)")
return true // Simulate successful delivery
}
}
// Facade class
class PizzaOrderFacade {
private let pizzaMaker = PizzaMaker()
private let paymentProcessor = PaymentProcessor()
private let deliveryService = DeliveryService()
func orderPizza(type: String, address: String, cardDetails: String) -> Bool {
let pizzaDetails = pizzaMaker.makePizza(type: type)
let paymentSuccessful = paymentProcessor.processPayment(amount: 15.0, cardDetails: cardDetails)
if paymentSuccessful {
let deliverySuccessful = deliveryService.deliverPizza(address: address, pizzaDetails: pizzaDetails)
return deliverySuccessful
} else {
print("Payment failed. Pizza order cancelled.")
return false
}
}
}
// Client code
let facade = PizzaOrderFacade()
let orderSuccessful = facade.orderPizza(type: "Pepperoni", address: "123 Main St", cardDetails: "4111-1234-5678-9012")
if orderSuccessful {
print("Pizza order completed successfully!")
}
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components. This makes the subsystem easier to use for clients who don’t need to know the details.
The Kotlin code demonstrates a Computer facade that encapsulates the complexities of starting up a computer (CPU, Memory, HardDrive, etc.). The client interacts solely with the Computer class, calling a single start() method instead of managing individual component initialization. This approach promotes loose coupling and simplifies client code. Kotlin’s concise syntax and class-based structure naturally lend themselves to the Facade pattern, allowing for a clean and readable implementation. The use of private constructors within the components further enforces encapsulation.
// Subsystem components
private class CPU {
fun freeze() {
println("CPU freezing...")
}
fun jump(position: Long) {
println("CPU jumping to position $position")
}
fun execute() {
println("CPU executing...")
}
}
private class Memory {
fun load(address: Long) {
println("Memory loading from address $address")
}
}
private class HardDrive {
fun read(sector: Int) {
println("Hard Drive reading sector $sector")
}
}
// Facade
class Computer {
private val cpu = CPU()
private val memory = Memory()
private val hardDrive = HardDrive()
fun start() {
println("Starting Computer...")
hardDrive.read(1)
memory.load(1024)
cpu.freeze()
cpu.jump(1000)
cpu.execute()
println("Computer started.")
}
}
// Client
fun main() {
val computer = Computer()
computer.start()
}
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components, making the subsystem easier to use. This example demonstrates a media player facade, simplifying interactions with audio and video components. The MediaPlayer struct encapsulates the complex logic of initializing and controlling the audio and video subsystems. Rust’s ownership and borrowing system naturally supports encapsulation, making it a good fit for this pattern. The use of structs and methods provides a clean and idiomatic way to define the facade interface.
// Subsystem components
struct AudioEngine {
loaded: bool,
}
impl AudioEngine {
fn new() -> Self {
AudioEngine { loaded: false }
}
fn load_audio(&mut self, filename: &str) {
println!("Loading audio file: {}", filename);
self.loaded = true;
}
fn play(&self) {
if self.loaded {
println!("Playing audio...");
} else {
println!("Audio not loaded.");
}
}
}
struct VideoEngine {
running: bool,
}
impl VideoEngine {
fn new() -> Self {
VideoEngine { running: false }
}
fn load_video(&mut self, filename: &str) {
println!("Loading video file: {}", filename);
}
fn play(&mut self) {
self.running = true;
println!("Playing video...");
}
fn stop(&mut self) {
self.running = false;
println!("Stopping video...");
}
}
// Facade
struct MediaPlayer {
audio_engine: AudioEngine,
video_engine: VideoEngine,
}
impl MediaPlayer {
fn new() -> Self {
MediaPlayer {
audio_engine: AudioEngine::new(),
video_engine: VideoEngine::new(),
}
}
fn play_media(&mut self, audio_file: &str, video_file: &str) {
self.audio_engine.load_audio(audio_file);
self.video_engine.load_video(video_file);
self.audio_engine.play();
self.video_engine.play();
}
fn stop_media(&mut self) {
self.video_engine.stop();
}
}
fn main() {
let mut player = MediaPlayer::new();
player.play_media("song.mp3", "movie.mp4");
player.stop_media();
}
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components. This makes the subsystem easier to use for clients who don’t need to know the details.
The Go code demonstrates a media player facade. The MediaPlayer struct encapsulates the complex interactions between different media components like Codec, VideoFile, and AudioFile. Clients interact solely with MediaPlayer’s Play() method, hiding the initialization and operation of these components. This implementation leverages Go’s struct composition and method receivers, which are idiomatic for creating such interfaces. Error handling is also included, a common practice in Go.
// main.go
package main
import (
"fmt"
"log"
)
// Subsystem components
type Codec interface {
Decode(data []byte) error
}
type VideoFile struct {
name string
}
type AudioFile struct {
name string
}
type VLCCodec struct{}
func (c *VLCCodec) Decode(data []byte) error {
fmt.Println("Decoding video with VLC codec")
return nil
}
type MP4AudioCodec struct{}
func (c *MP4AudioCodec) Decode(data []byte) error {
fmt.Println("Decoding audio with MP4 codec")
return nil
}
// Facade
type MediaPlayer struct {
videoFile *VideoFile
audioFile *AudioFile
videoCodec Codec
audioCodec Codec
}
func NewMediaPlayer(videoFileName, audioFileName string) *MediaPlayer {
return &MediaPlayer{
videoFile: &VideoFile{name: videoFileName},
audioFile: &AudioFile{name: audioFileName},
videoCodec: &VLCCodec{},
audioCodec: &MP4AudioCodec{},
}
}
func (mp *MediaPlayer) Play() error {
fmt.Println("Playing media...")
videoData, err := mp.getVideoData()
if err != nil {
return fmt.Errorf("error getting video data: %w", err)
}
audioData, err := mp.getAudioData()
if err != nil {
return fmt.Errorf("error getting audio data: %w", err)
}
if err := mp.videoCodec.Decode(videoData); err != nil {
return fmt.Errorf("error decoding video: %w", err)
}
if err := mp.audioCodec.Decode(audioData); err != nil {
return fmt.Errorf("error decoding audio: %w", err)
}
fmt.Println("Media playing successfully.")
return nil
}
func (mp *MediaPlayer) getVideoData() ([]byte, error) {
fmt.Printf("Loading video file: %s\n", mp.videoFile.name)
// Simulate loading video data
return []byte("video data"), nil
}
func (mp *MediaPlayer) getAudioData() ([]byte, error) {
fmt.Printf("Loading audio file: %s\n", mp.audioFile.name)
// Simulate loading audio data
return []byte("audio data"), nil
}
func main() {
player := NewMediaPlayer("movie.mp4", "sound.mp3")
err := player.Play()
if err != nil {
log.Fatalf("Failed to play media: %v", err)
}
}
The Facade pattern provides a simplified interface to a complex subsystem. It encapsulates multiple interactions within the subsystem into a single, higher-level interface, making the system easier to use. This example demonstrates a media player facade, hiding the complexities of audio and video components. The MediaPlayerFacade provides methods like play() and stop() which internally handle the initialization, playback, and cleanup of the underlying AudioPlayer and VideoPlayer subsystems. This is idiomatic C as it leverages structs to represent the facade and subsystems, and function pointers to define the interfaces, keeping the code modular and manageable.
#include <stdio.h>
#include <stdlib.h>
// Subsystem 1: Audio Player
typedef struct {
void (*start)(void);
void (*stop)(void);
} AudioPlayer;
void audio_start(void) {
printf("Audio: Starting playback...\n");
}
void audio_stop(void) {
printf("Audio: Stopping playback...\n");
}
AudioPlayer audio_player = {
.start = audio_start,
.stop = audio_stop
};
// Subsystem 2: Video Player
typedef struct {
void (*start)(void);
void (*stop)(void);
} VideoPlayer;
void video_start(void) {
printf("Video: Starting playback...\n");
}
void video_stop(void) {
printf("Video: Stopping playback...\n");
}
VideoPlayer video_player = {
.start = video_start,
.stop = video_stop
};
// Facade: Media Player
typedef struct {
AudioPlayer *audio;
VideoPlayer *video;
} MediaPlayerFacade;
void media_player_start(MediaPlayerFacade *facade) {
if (facade == NULL) return;
facade->audio->start();
facade->video->start();
}
void media_player_stop(MediaPlayerFacade *facade) {
if (facade == NULL) return;
facade->audio->stop();
facade->video->stop();
}
MediaPlayerFacade create_media_player_facade() {
MediaPlayerFacade facade;
facade.audio = &audio_player;
facade.video = &video_player;
return facade;
}
int main() {
MediaPlayerFacade player = create_media_player_facade();
media_player_start(&player);
// Simulate playback
printf("Playing media...\n");
media_player_stop(&player);
return 0;
}
The Facade pattern provides a simplified interface to a complex subsystem. It encapsulates multiple interactions within the subsystem into a single, higher-level interface, making it easier for clients to use. This example demonstrates a media player facade, hiding the complexities of audio and video components. The MediaFacade class provides methods like playMovie and playMusic, internally coordinating the AudioPlayer and VideoPlayer to achieve the desired functionality. This approach aligns with C++’s object-oriented principles, promoting encapsulation and reducing coupling between the client code and the subsystem.
#include <iostream>
#include <string>
// Subsystem classes
class AudioPlayer {
public:
void play(const std::string& filename) {
std::cout << "Playing audio: " << filename << std::endl;
}
};
class VideoPlayer {
public:
void play(const std::string& filename) {
std::cout << "Playing video: " << filename << std::endl;
}
};
class DVDPlayer {
public:
void on() {
std::cout << "DVD Player is ON" << std::endl;
}
void off() {
std::cout << "DVD Player is OFF" << std::endl;
}
void playDVD(const std::string& dvdName) {
std::cout << "Playing DVD: " << dvdName << std::endl;
}
};
// Facade class
class MediaFacade {
private:
AudioPlayer audioPlayer;
VideoPlayer videoPlayer;
DVDPlayer dvdPlayer;
public:
void playMovie(const std::string& filename) {
dvdPlayer.on();
dvdPlayer.playDVD(filename);
videoPlayer.play(filename);
audioPlayer.play(filename);
dvdPlayer.off();
}
void playMusic(const std::string& filename) {
audioPlayer.play(filename);
}
};
int main() {
MediaFacade mediaFacade;
mediaFacade.playMovie("The Matrix");
std::cout << std::endl;
mediaFacade.playMusic("Bohemian Rhapsody");
return 0;
}
The Facade pattern provides a simplified interface to a complex subsystem. It encapsulates multiple interactions within the subsystem into a single, higher-level interface, hiding the complexity from the client. This example demonstrates a media player facade, simplifying operations like playing music, video, and managing the audio/video components. The code uses C#’s class-based OOP approach to define the facade and the subsystem components. It’s idiomatic C# due to its clear class structure, property usage, and method naming conventions, promoting encapsulation and ease of use.
// Subsystem classes
public class AudioEngine
{
public void Play(string fileName) => Console.WriteLine($"Audio: Playing {fileName}");
public void Stop() => Console.WriteLine("Audio: Stopped");
}
public class VideoEngine
{
public void Play(string fileName) => Console.WriteLine($"Video: Playing {fileName}");
public void Stop() => Console.WriteLine("Video: Stopped");
}
// Facade class
public class MediaFacade
{
private readonly AudioEngine _audioEngine = new();
private readonly VideoEngine _videoEngine = new();
public void PlayMedia(string fileName, MediaType type)
{
Console.WriteLine($"MediaFacade: Playing {fileName} ({type})");
switch (type)
{
case MediaType.Audio:
_audioEngine.Play(fileName);
break;
case MediaType.Video:
_videoEngine.Play(fileName);
break;
default:
Console.WriteLine("MediaFacade: Unsupported media type.");
break;
}
}
public void StopMedia()
{
Console.WriteLine("MediaFacade: Stopping media");
_audioEngine.Stop();
_videoEngine.Stop();
}
}
// Enum for media types
public enum MediaType
{
Audio,
Video
}
// Client code
public class Client
{
public static void Main(string[] args)
{
MediaFacade facade = new();
facade.PlayMedia("song.mp3", MediaType.Audio);
facade.PlayMedia("movie.mp4", MediaType.Video);
facade.StopMedia();
}
}
The Facade pattern provides a simplified interface to a complex subsystem. It hides the intricacies of the underlying components and offers a higher-level, easier-to-use API. This improves code readability and reduces dependencies between the client code and the subsystem.
The TypeScript example demonstrates a VideoConversionFacade that encapsulates the complexities of video file processing – including reading the file, decoding it, applying filters, and encoding it to a new format. The client only interacts with the facade, unaware of the individual steps. This implementation leverages TypeScript’s class-based structure and type safety for a clean and maintainable facade. It’s idiomatic because it uses clear interfaces and avoids exposing internal implementation details.
// Subsystem classes
class VideoFileReader {
read(filename: string): string {
console.log(`Reading video file: ${filename}`);
return `Video data from ${filename}`;
}
}
class VideoDecoder {
decode(data: string): string {
console.log("Decoding video data...");
return `Decoded video data from ${data}`;
}
}
class VideoFilter {
apply(data: string): string {
console.log("Applying video filters...");
return `Filtered video data from ${data}`;
}
}
class VideoEncoder {
encode(data: string, format: string): string {
console.log(`Encoding video data to ${format}...`);
return `Encoded video data in ${format} from ${data}`;
}
}
// Facade class
class VideoConversionFacade {
private reader: VideoFileReader;
private decoder: VideoDecoder;
private filter: VideoFilter;
private encoder: VideoEncoder;
constructor() {
this.reader = new VideoFileReader();
this.decoder = new VideoDecoder();
this.filter = new VideoFilter();
this.encoder = new VideoEncoder();
}
convert(filename: string, format: string): string {
const data = this.reader.read(filename);
const decodedData = this.decoder.decode(data);
const filteredData = this.filter.apply(decodedData);
const encodedData = this.encoder.encode(filteredData, format);
return encodedData;
}
}
// Client code
const facade = new VideoConversionFacade();
const result = facade.convert("input.mp4", "avi");
console.log(result);
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components, making the system easier to use. This example demonstrates a facade for ordering a pizza, encapsulating the complexities of checking ingredients, preparing the dough, adding toppings, and baking. The PizzaOrderFacade provides a single orderPizza method, hiding the internal steps. This aligns with JavaScript’s flexible nature, allowing for easy object composition and a clean, readable API.
// Subsystem classes
class Dough {
prepare() {
console.log("Preparing the dough...");
}
}
class Sauce {
addSauce() {
console.log("Adding tomato sauce...");
}
}
class Toppings {
add(topping) {
console.log(`Adding ${topping}...`);
}
}
class Baking {
bake() {
console.log("Baking the pizza...");
}
}
// Facade class
class PizzaOrderFacade {
constructor() {
this.dough = new Dough();
this.sauce = new Sauce();
this.toppings = new Toppings();
this.baking = new Baking();
}
orderPizza(topping1, topping2) {
this.dough.prepare();
this.sauce.addSauce();
this.toppings.add(topping1);
this.toppings.add(topping2);
this.baking.bake();
console.log("Pizza is ready!");
}
}
// Usage
const order = new PizzaOrderFacade();
order.orderPizza("pepperoni", "cheese");
The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the intricacies of the underlying components, making the system easier to use. This example demonstrates a simple media player facade, abstracting away the complexities of handling different media types (audio and video). The MediaFacade class provides a single play method, handling the instantiation and interaction with the appropriate media player based on the file extension. This approach aligns with Python’s emphasis on readability and reducing complexity through abstraction.
# Subsystem classes
class AudioPlayer:
def play(self, filename):
print(f"Playing audio file: {filename}")
class VideoPlayer:
def play(self, filename):
print(f"Playing video file: {filename}")
class FileTypeChecker:
def is_audio(self, filename):
return filename.endswith(".mp3") or filename.endswith(".wav")
def is_video(self, filename):
return filename.endswith(".mp4") or filename.endswith(".avi")
# Facade class
class MediaFacade:
def __init__(self):
self.audio_player = AudioPlayer()
self.video_player = VideoPlayer()
self.file_checker = FileTypeChecker()
def play(self, filename):
if self.file_checker.is_audio(filename):
self.audio_player.play(filename)
elif self.file_checker.is_video(filename):
self.video_player.play(filename)
else:
print(f"Unsupported file type: {filename}")
# Client code
if __name__ == "__main__":
facade = MediaFacade()
facade.play("song.mp3")
facade.play("movie.mp4")
facade.play("document.pdf")
The Facade pattern provides a simplified interface to a complex subsystem. It encapsulates multiple interactions within the subsystem into a single, higher-level interface, making it easier for clients to use. This example demonstrates a facade for a home theater system. The HomeTheaterFacade class provides methods like watchMovie() and endMovie() which internally coordinate the actions of various components (lights, projector, sound system, etc.). This shields the client from needing to know the intricacies of controlling each component individually. The Java implementation uses clear class definitions and method calls, aligning with the language’s object-oriented principles and promoting loose coupling.
// Subsystem classes
class Lights {
public void on() {
System.out.println("Lights turned on");
}
public void off() {
System.out.println("Lights turned off");
}
}
class Projector {
public void on() {
System.out.println("Projector turned on");
}
public void off() {
System.out.println("Projector turned off");
}
}
class SoundSystem {
public void on() {
System.out.println("Sound System turned on");
}
public void off() {
System.out.println("Sound System turned off");
}
public void setVolume(int level) {
System.out.println("Volume set to " + level);
}
}
// Facade class
class HomeTheaterFacade {
private Lights lights;
private Projector projector;
private SoundSystem soundSystem;
public HomeTheaterFacade() {
this.lights = new Lights();
this.projector = new Projector();
this.soundSystem = new SoundSystem();
}
public void watchMovie(String movie) {
System.out.println("Watching " + movie);
lights.off();
projector.on();
soundSystem.on();
soundSystem.setVolume(50);
}
public void endMovie() {
System.out.println("Movie finished");
lights.on();
projector.off();
soundSystem.off();
}
}
// Client code
public class FacadeExample {
public static void main(String[] args) {
HomeTheaterFacade homeTheater = new HomeTheaterFacade();
homeTheater.watchMovie("Star Wars");
homeTheater.endMovie();
}
}