Patrones de diseño – Facade

Hoy os traemos el patrón de diseño Facade (façade), o Fachada. Junto con el Adapter, este patrón forma parte de los patrones estructurales.

El cometido del patrón Facade es hacer de intermediario (de fachada) entre los consumidores y distintos sistemas. En lugar de que el consumidor tenga que hablar con 2 o 3 sistemas para hacer una acción, habla con la fachada y esta ya se encargará de hablar con los sistemas.

Esto nos permite simplificar el código del consumidor y a su vez reciclar este comportamiento, ya que podrían haber otros consumidores hablando también con estos sistemas. Esto no quita que algún consumidor pueda hablar directamente con un sistema sin utilizar la fachada.

Algunas ventajas de utilizar este patrón serían:

  1. Ocultar a los consumidores los componentes del subsistema, así logramos reducir el número de dependencias de los consumidores.
  2. Promueve un bajo acoplamiento con los subsistemas. Al ser la fachada la que habla directamente con los subsistemas los consumidores no se acoplan a ellos, con lo que nos facilita el cambio.

Una desventaja de este patrón podría ser que la fachada proporcionará más funciones de las que algún consumidor necesita, pero esto lo podemos subsanar aplicando el principio de Segregación de Interfaces (ISP) de SOLID.

Esta imagen que podemos ver abajo sintetiza muy bien el concepto del patrón Facade.

patron fachada

Vamos a ver ahora un ejemplo de como podemos aplicar este patrón en nuestro juego, o aplicación.

Sistema de batalla con el patrón Facade

Vamos a suponer que estamos trabajando en un RPG por turnos y nos toca hacer la batalla, en concreto como empezamos y terminamos la batalla.

Para empezar la batalla mostraremos primero un ScreenFade, instanciaremos a los aliados y enemigos, mostraremos la UI y ocultaremos el ScreenFade. Para terminar la batalla tendremos que hacer lo mismo pero a la inversa, mostraremos el ScreenFade, destruiremos a los héroes y ocultaremos la UI.

Como te debes de estar imaginando, estas dos acciones utilizarán los mismos sistemas para realizar sus acciones. Tendremos el sistema de instanciado de héroes, el sistema de UI de héroes, y el sistema de transiciones con el ScreenFade.

Una primera aproximación podría ser la siguiente:

patron de diseño estructural

Un poco lío ¿verdad? Y eso que solo tenemos dos consumidores utilizando estos sistemas, imaginaros el caos con algo más grande.

Por no hablar del fuerte acoplamiento de los consumidores con los sistemas, esto nos dificultará muchísimo cuando queramos cambiar los sistemas, porque un cambio en el HeroSpawner podría repercutir en todos sus consumidores.

Si en lugar de dejar que cualquier consumidor hable con estos sistemas aplicamos el patrón de diseño Facade, estaremos reduciendo el número de dependencias de estos consumidores y facilitando cambios futuros, y eso siempre es algo que está muy bien 😃.

Vamos a ver como sería el diagrama con la Fachada:

facade patron de diseño

Como veis ahora el diagrama queda mucho más claro y solo es una clase, el Facade, quien se acopla a estos sistemas.

Obviamente esto nos da una clase más que mantener, pero a cambio de desacoplar las otras de los sistemas y facilitando muchísimo el mantenimiento de estas. Desde luego yo prefiero tener una clase más y que el mantenimiento de las otras no suponga un dolor 😉.

Implementación de la Fachada

Sistemas

Vamos con la implementación de este patron estructural, empezaremos por los sistemas:

public class ScreenFade : MonoBehaviour
    {
        public void Show() {...}
        public void Hide() {...}
    }
public class HeroUi : MonoBehaviour
    {
        public void ShowAlliesUi() {...}
        public void HideAlliesUi() {...}
        public void ShowEnemiesUi() {...}
        public void HideEnemiesUi() {...}
    }
public class HeroSpawner : MonoBehaviour
    {
        public void SpawnAllies() {...}
        public void DestroyAllies() {...}
        public void SpawnEnemies() {...}
        public void DestroyEnemies() {...}
    }

He omitido todos los cuerpos de las funciones de los sistemas para facilitar la lectura, pero te puedes descargar el ejemplo completo AQUÍ.

Facade y consumidores

Como dijimos anteriormente, la fachada será la clase que consumirá a los sistemas y publicará funciones para facilitar el uso:

public class BattleFacade : MonoBehaviour
    {
        [SerializeField] private HeroSpawner _heroSpawner;
        [SerializeField] private HeroUi      _heroUi;
        [SerializeField] private ScreenFade  _screenFade;

        public void StartBattle()
        {
            _screenFade.Show();
            _heroSpawner.SpawnAllies();
            _heroSpawner.SpawnEnemies();
            _heroUi.ShowAlliesUi();
            _heroUi.ShowEnemiesUi();
            _screenFade.Hide();
        }

        public void EndBattle()
        {
            _screenFade.Show();
            _heroSpawner.DestroyAllies();
            _heroSpawner.DestroyEnemies();
            _heroUi.HideAlliesUi();
            _heroUi.HideEnemiesUi();
            _screenFade.Hide();
        }
    }

Ahora vamos a ver los consumidores, el código qué veis comentado es el que tendríamos de no estar aplicando el patrón Facade.

public class StartBattleConsumer : MonoBehaviour
{
    /* [SerializeField] private HeroSpawner _heroSpawner;
    [SerializeField] private HeroUi      _heroUi;
    [SerializeField] private ScreenFade  _screenFade; */

    [SerializeField] private BattleFacade _battleFacade;

    private void Update()
    {
        if (Input.GetKey(KeyCode.F1))
        {
            /* _screenFade.Show();
            _heroSpawner.SpawnAllies();
            _heroSpawner.SpawnEnemies();
            _heroUi.ShowAlliesUi();
            _heroUi.ShowEnemiesUi();
            _screenFade.Hide(); */

            _battleFacade.StartBattle();
        }
    }
}
public class EndBattleConsumer : MonoBehaviour
{
    /* [SerializeField] private HeroSpawner _heroSpawner;
    [SerializeField] private HeroUi      _heroUi;
    [SerializeField] private ScreenFade  _screenFade; */

    [SerializeField] private BattleFacade _battleFacade;

    private void Update()
    {
        if (Input.GetKey(KeyCode.F2))
        {
            /* _screenFade.Show();
            _heroSpawner.DestroyAllies();
            _heroSpawner.DestroyEnemies();
            _heroUi.HideAlliesUi();
            _heroUi.HideEnemiesUi();
            _screenFade.Hide(); */

            _battleFacade.EndBattle();
        }
    }
}

Y cuando ejecutamos el proyecto y pulsamos F1 tenemos una épica batalla 😃. Podéis descargar el proyecto en este enlace.

façade aplicado

Conclusión

En este post hemos visto los beneficios de utilizar el patrón estructural Facade, como añadiendo una clase que actue de intermediaria entre los consumidores y los distintos sistemas nos puede facilitar el uso de los mismos.

Además esto reduce el número de dependencias de los consumidores, con lo que modificar los sistemas nos será mucho más fácil.

Como siempre, os podéis descargar el proyecto completo en Unity desde este enlace. Y no olvidéis dejarnos un comentario con cualquier duda, o sugerencia, que os surja así también estaréis ayudando a otra gente que pudiese tener la misma inquietud.

Fuentes

Otros patrones de diseño

Resumen
➤ Patrones de diseño - Facade
Nombre del artículo
➤ Patrones de diseño - Facade
Descripción
¿Cansado de que tus clases estén acopladas a todo? El PATRÓN de DISEÑO estructural FACADE 🏪 puede ser la solución que estás buscando 🧐.
Autor
Publisher Name
The Power Ups - Learning
Publisher Logo