Fachada (Facade) es un tipo de patrón de diseño estructural. Viene motivado por la necesidad de estructurar un entorno de programación y reducir su complejidad con la división en subsistemas, minimizando las comunicaciones y dependencias entre estos.
Se aplicará el patrón fachada cuando se necesite proporcionar una interfaz simple para un subsistema complejo, o cuando se quiera estructurar varios subsistemas en capas, ya que las fachadas serían el punto de entrada a cada nivel. Otro escenario proclive para su aplicación surge de la necesidad de desacoplar un sistema de sus clientes y de otros subsistemas, haciéndolo más independiente, portable y reutilizable (esto es, reduciendo dependencias entre los subsistemas y los clientes).
Se puede ver en la siguiente figura:
A continuación, se muestra un ejemplo:
Fachada (Facade): conoce qué clases del subsistema son responsables de una determinada petición, y delega esas peticiones de los clientes a los objetos apropiados del subsistema.
Subclases (ModuleA, ModuleB, ModuleC...): implementan la funcionalidad del subsistema. Realizan el trabajo solicitado por la fachada. No conocen la existencia de la fachada.
Los clientes que se comunican con el subsistema enviando peticiones al objeto Fachada, el cual las reenvía a los objetos apropiados del subsistema.
Los objetos del subsistema realizan el trabajo final, y la fachada hace algo de trabajo para pasar de su interfaz a las del subsistema.
Los clientes que usan la fachada no tienen que acceder directamente a los objetos del subsistema.
La principal ventaja del patrón fachada consiste en que para modificar las clases de los subsistemas, sólo hay que realizar cambios en la interfaz/fachada, y los clientes pueden permanecer ajenos a ello. Además, y como se mencionó anteriormente, los clientes no necesitan conocer las clases que hay tras dicha interfaz.
Como inconveniente, si se considera el caso de que varios clientes necesiten acceder a subconjuntos diferentes de la funcionalidad que provee el sistema, podrían acabar usando sólo una pequeña parte de la fachada, por lo que sería conveniente utilizar varias fachadas más específicas en lugar de una única global.
Uno de los patrones relacionados más directamente es el singleton, dado que en determinadas ocasiones las fachadas pueden ser instancias únicas.
Otros patrones que guardan una cierta relación con el patrón fachada son los GRASP (General Responsibility Assignment Software Patterns), los cuales no son patrones de diseño, sino buenas prácticas que guían al desarrollador para encontrar los patrones de diseño, que son más concretos. Uno de los patrones GRASP es un controlador que actúa como punto de entrada en la capa lógica, lo que se puede comparar perfectamente con el uso del patrón fachada.
Problema: Un cliente necesita acceder a parte de la funcionalidad de un sistema más complejo.
Problema: Existen grupos de tareas muy frecuentes para las que se puede crear código más sencillo y legible.
Problema: Una biblioteca es difícilmente legible.
Problema: Dependencia entre el código del cliente y la parte interna de una biblioteca.
Problema: Necesidad de acceder a un conjunto de APIs que pueden además tener un diseño no muy bueno.
Problema: Muchas clases cliente quieren usar varias clases servidoras, y deben saber cuál es exactamente la que le proporciona cada servicio. El sistema se volvería muy complejo, porque habría que relacionar todas las clases cliente con todas y cada una de las clases servidoras.
En Java las clases java.awt.Graphics y java.awt.Font.
En el siguiente ejemplo implementado en java, se puede visualizar como se resuelve el problema de hacer más legible y no repetir código para las tareas más frecuentes.
package com.genbetadev;
public class Impresora {
private String tipoDocumento;
private String hoja;
private boolean color;
private String texto;
public String getTipoDocumento() {
return tipoDocumento;
}
public void setTipoDocumento(String tipoDocumento) {
this.tipoDocumento = tipoDocumento;
}
public void setHoja(String hoja) {
this.hoja = hoja;
}
public String getHoja() {
return hoja;
}
public void setColor(boolean color) {
this.color = color;
}
public boolean getColor() {
return color;
}
public void setTexto(String texto) {
this.texto = texto;
}
public String getTexto() {
return texto;
}
public void imprimir() {
impresora.imprimirDocumento();
}
}
Se trata de una clase sencilla que imprime documentos en uno u otro formato. El código de la clase cliente nos ayudará a entender mejor su funcionamiento.
package com.genbetadev;
public class PrincipalCliente {
public static void main(String[] args) {
Impresora i = new Impresora();
i.setHoja("a4");
i.setColor(true);
i.setTipoDocumento("pdf");
i.setTexto("texto 1");
i.imprimirDocumento();
Impresora i2 = new Impresora();
i2.setHoja("a4");
i2.setColor(true);
i2.setTipoDocumento("pdf");
i2.setTexto("texto 2");
i2.imprimirDocumento();
Impresora i3 = new Impresora();
i3.setHoja("a3");
i3.setColor(false);
i3.setTipoDocumento("excel");
i3.setTexto("texto 3");
i3.imprimirDocumento();
}
}
Como podemos ver la clase cliente se encarga de invocar a la impresora, y configurarla para después imprimir varios documentos .Ahora bien prácticamente todos los documentos que escribimos tienen la misma estructura (formato A4, Color , PDF). Estamos continuamente repitiendo código. Vamos a construir una nueva clase FachadaImpresoraNormal que simplifique la impresión de documentos que sean los más habituales.
package com.genbetadev;
public class FachadaImpresoraNormal {
Impresora impresora;
public FachadaImpresoraNormal(String texto) {
super();
impresora= new Impresora();
impresora.setColor(true);
impresora.setHoja("A4");
impresora.setTipoDocumento("PDF");
impresora.setTexto(texto);
}
public void imprimir() {
impresora.imprimirDocumento();
}
}
De esta forma el cliente quedará mucho más sencillo :
package com.genbetadev;
public class PrincipalCliente2 {
public static void main(String[] args) {
FachadaImpresoraNormal fachada1= new FachadaImpresoraNormal("texto1");
fachada1.imprimir();
FachadaImpresoraNormal fachada2= new FachadaImpresoraNormal("texto2");
fachada2.imprimir();
Impresora i3 = new Impresora();
i3.setHoja("a4");
i3.setColor(true);
i3.setTipoDocumento("excel");
i3.setTexto("texto 3");
i3.imprimirDocumento();
}
}