Design Patterns - Facade

Propósito

El patrón Facade (Fachada) proporciona una interfaz unificada y simplificada a un conjunto de interfaces en un subsistema. 

Facilita el uso a los clientes de sistemas grandes y complejos.

facade facade

Problema

El problema que resuelve el Facade es la complejidad asociada a los sistemas que tienen múltiples componentes interdependientes o sub-sistemas con sus propias interfaces.

Estos sistemas pueden ser difíciles de entender y usar para los clientes, sobre todo cuando solo necesitan una parte de su funcionalidad.

Esto también genera dependencia de los clientes, lo que hace que el subsistema se vuelva difícil de cambiar.

Solución

La solución que propone el Facade es:

  • Interfaz Unificada: Proporciona una interfaz única y simplificada para un conjunto de interfaces en un subsistema. Esto permite a los usuarios interactuar con el subsistema de una manera mucho más sencilla.
  • Reducción del Acoplamiento: Al usar la fachada, los clientes no necesitan conocer los detalles internos del subsistema, lo que reduce el acoplamiento.

Algo importante, la fachada puede hacer trabajo propio en caso de ser necesario.

Estructura

facade

Participantes

  • Facade: Sabe qué clases del subsistema son responsables ante una petición, por lo que delega las peticiones en los objetos apropiados del subsistema.
  • Subsystems Class: Implementa la funcionalidad del subsistema. Realizan las labores encomendadas por el objeto Facade, aunque ellos no lo conocen, no tienen referencias a él.
  • Client: Accede al subsistema a través del objeto Facade.

Cuándo Usarlo

Este patrón es recomendable cuando:

  • queremos proporcionar una interfaz simple a un subsistema complejo.
  • hay muchas dependencias entre clientes y clases de implementación.

Ventajas

Simplicidad para el Cliente: Facilita a los clientes el uso del subsistema.

Reduce la Complejidad: Oculta la complejidad del subsistema detrás de una interfaz simple.

Desacoplamiento: Reduce el acoplamiento entre el subsistema y sus clientes.

Desventajas

Riesgo de Convertirse en un Objeto ‘Dios’: La fachada podría acabar con demasiada funcionalidad, convirtiéndose en un objeto que hace demasiado.

Limitación de Funcionalidades: Algunos usuarios pueden necesitar más funcionalidades de las que ofrece la fachada.

Ejemplo: Sistema de Home Theater

Estamos desarrollando un sistema integrado para un home theater que consta de varios componentes…televisión, reproductor de BluRay, sistema de sonido envolvente.

Cada uno de estos componentes tiene su propia interfaz y su propio conjunto de operaciones.

Problema

Los usuarios se enfrentan a la complejidad de manejar múltiples dispositivos con interfaces diferentes. Para realizar una tarea simple, como ver una película, deben encender cada dispositivo, la televisión, el sistema de sonido, el reproductor de BluRay.

Esto resulta en una experiencia de usuario complicada y tediosa.

Solución planteada

Decidimos usar el patrón Facade para brindar una interfaz unificada y simple para la compleja lógica del sistema de home theater.

El Facade va a encapsular las interacciones entre los diversos componentes del sistema, facilitando a los usuarios realizar tareas comunes mediante una serie de operaciones simples.

Tenemos las clases del subsistema, cada una con su propia interfaz, y creamos HomeTheaterFacade que brinda y encapsula una serie de funcionalidades comúnmente usadas por los clientes.

facade

 

Código Java

Codificamos en Java lo que preparamos en el diagrama.

Tenemos las clases del subsistema:


    // Subsystem Classes
    class Television {
        void turnOn() {
            System.out.println("Televisión encendida.");
        }
    
        void turnOff() {
            System.out.println("Televisión apagada.");
        }
    }
    
    class SoundSystem {
        void turnOn() {
            System.out.println("Sistema de sonido encendido.");
        }
    
        void turnOff() {
            System.out.println("Sistema de sonido apagado.");
        }
    }
    
    class BluRayPlayer {
        void turnOn() {
            System.out.println("Reproductor Blu-ray encendido.");
        }
    
        void play() {
            System.out.println("Reproduciendo película.");
        }
    
        void turnOff() {
            System.out.println("Reproductor Blu-ray apagado.");
        }
    }
              

Creamos la clase HomeTheaterFacade:


    class HomeTheaterFacade {
        private Television tv;
        private SoundSystem soundSystem;
        private BluRayPlayer bluRay;
    
        public HomeTheaterFacade(Television tv, SoundSystem soundSystem, BluRayPlayer bluRay) {
            this.tv = tv;
            this.soundSystem = soundSystem;
            this.bluRay = bluRay;
        }
    
        void watchMovie() {
            tv.turnOn();
            soundSystem.turnOn();
            bluRay.turnOn();
            bluRay.play();
        }
    
        void endMovie() {
            bluRay.turnOff();
            soundSystem.turnOff();
            tv.turnOff();
        }
    }
              

El código cliente que mira una película:


    public class Client {
        public static void main(String[] args) {
            Television tv = new Television();
            SoundSystem soundSystem = new SoundSystem();
            BluRayPlayer bluRay = new BluRayPlayer();
            HomeTheaterFacade facade = new HomeTheaterFacade(tv, soundSystem, bluRay);
    
            // Ver una película
            facade.watchMovie();
    
            // Terminar de ver la película
            facade.endMovie();
        }
    }
              

Mapeo (del ejemplo a Participantes)

Los participantes que vimos antes son: Subsystem Classes, Facade, Client:

  • Television, SoundSystem, BlueRayPlayer (Subsystem Classes): Clases del subsistema
  • HomeTheaterFacade(Facade): Clase con la que interactúa el cliente, encapsula funcionalidades concretas facilitando el uso.
  • Client (Client): Usa el Facade para interfactuar con el subsistema.

Conclusiones

El patrón Facade simplifica mucho la interacción del usuario con el sistema de home theater. Al brindar una interfaz sencilla y unificada, ocultamos la complejidad de operar múltiples dispositivos, lo que mejora la experiencia del usuario y facilita la gestión del sistema.

Además, si agregamos otro dispositivo, como un proyector, solo hay que modificar el Facade, evita tener que hacer el cambio en cada uno de los clientes que usan el subsistema.

Patrones relacionados

  • Abstract Factory

Pueden se usados con Facade para construir un subsistema.

  • Singleton

A veces el Facade se implementa como un Singleton.

  • Mediator

Se parece al Facade en el sentido de que abstrae funcionalidad de clases existentes, pero tienen propósitos diferenes, ya que el Mediator tiene como objetivo abstraer cualquier comunicación entre objetos similares, centralizando la funcionalidad que no pertenece a ninguno de ellos.