Facade es un patrón de diseño estructural que proporciona una interfaz simplificada (pero limitada) a un sistema complejo de clases, bibliotecas o _frameworks_.
El patrón Facade disminuye la complejidad general de la aplicación, al mismo tiempo que ayuda a mover dependencias no deseadas a un solo lugar.
Ejemplo conceptual
Resulta sencillo subestimar las complejidades que tienen lugar tras bambalinas cuando pides una pizza con tu tarjeta de crédito. Decenas de subsistemas actúan en este proceso. Aquí tienes una pequeña muestra de ellos:
Comprobar la cuenta
Comprobar el PIN de seguridad
Balance de crédito/débito
Realizar la entrada del movimiento
Enviar la notificación
En un sistema complejo como éste, es fácil perderse y es fácil descomponer algo si se hacen las cosas mal. Por eso existe el concepto del patrón Facade: algo que permite al cliente trabajar con decenas de componentes utilizando una interfaz simple. El cliente solo tiene que introducir los datos de la tarjeta, el pin de seguridad, la cantidad a pagar y el tipo de operación. El patrón Facade dirige las comunicaciones con varios componentes sin exponer al cliente a las complejidades internas.
walletFacade.go: Fachada
package main
import "fmt"
type WalletFacade struct {
account *Account
wallet *Wallet
securityCode *SecurityCode
notification *Notification
ledger *Ledger
}
func newWalletFacade(accountID string, code int) *WalletFacade {
fmt.Println("Starting create account")
walletFacacde := &WalletFacade{
account: newAccount(accountID),
securityCode: newSecurityCode(code),
wallet: newWallet(),
notification: &Notification{},
ledger: &Ledger{},
}
fmt.Println("Account created")
return walletFacacde
}
func (w *WalletFacade) addMoneyToWallet(accountID string, securityCode int, amount int) error {
fmt.Println("Starting add money to wallet")
err := w.account.checkAccount(accountID)
if err != nil {
return err
}
err = w.securityCode.checkCode(securityCode)
if err != nil {
return err
}
w.wallet.creditBalance(amount)
w.notification.sendWalletCreditNotification()
w.ledger.makeEntry(accountID, "credit", amount)
return nil
}
func (w *WalletFacade) deductMoneyFromWallet(accountID string, securityCode int, amount int) error {
fmt.Println("Starting debit money from wallet")
err := w.account.checkAccount(accountID)
if err != nil {
return err
}
err = w.securityCode.checkCode(securityCode)
if err != nil {
return err
}
err = w.wallet.debitBalance(amount)
if err != nil {
return err
}
w.notification.sendWalletDebitNotification()
w.ledger.makeEntry(accountID, "debit", amount)
return nil
}
account.go: Partes del subsistema complejo
package main
import "fmt"
type Account struct {
name string
}
func newAccount(accountName string) *Account {
return &Account{
name: accountName,
}
}
func (a *Account) checkAccount(accountName string) error {
if a.name != accountName {
return fmt.Errorf("Account Name is incorrect")
}
fmt.Println("Account Verified")
return nil
}
securityCode.go: Partes del subsistema complejo
package main
import "fmt"
type SecurityCode struct {
code int
}
func newSecurityCode(code int) *SecurityCode {
return &SecurityCode{
code: code,
}
}
func (s *SecurityCode) checkCode(incomingCode int) error {
if s.code != incomingCode {
return fmt.Errorf("Security Code is incorrect")
}
fmt.Println("SecurityCode Verified")
return nil
}
wallet.go: Partes del subsistema complejo
package main
import "fmt"
type Wallet struct {
balance int
}
func newWallet() *Wallet {
return &Wallet{
balance: 0,
}
}
func (w *Wallet) creditBalance(amount int) {
w.balance += amount
fmt.Println("Wallet balance added successfully")
return
}
func (w *Wallet) debitBalance(amount int) error {
if w.balance < amount {
return fmt.Errorf("Balance is not sufficient")
}
fmt.Println("Wallet balance is Sufficient")
w.balance = w.balance - amount
return nil
}
ledger.go: Partes del subsistema complejo
package main
import "fmt"
type Ledger struct {
}
func (s *Ledger) makeEntry(accountID, txnType string, amount int) {
fmt.Printf("Make ledger entry for accountId %s with txnType %s for amount %d\n", accountID, txnType, amount)
return
}
notification.go: Partes del subsistema complejo
package main
import "fmt"
type Notification struct {
}
func (n *Notification) sendWalletCreditNotification() {
fmt.Println("Sending wallet credit notification")
}
func (n *Notification) sendWalletDebitNotification() {
fmt.Println("Sending wallet debit notification")
}
main.go: Código cliente
package main
import (
"fmt"
"log"
)
func main() {
fmt.Println()
walletFacade := newWalletFacade("abc", 1234)
fmt.Println()
err := walletFacade.addMoneyToWallet("abc", 1234, 10)
if err != nil {
log.Fatalf("Error: %s\n", err.Error())
}
fmt.Println()
err = walletFacade.deductMoneyFromWallet("abc", 1234, 5)
if err != nil {
log.Fatalf("Error: %s\n", err.Error())
}
}
output.txt: Resultado de la ejecución
Starting create account
Account created
Starting add money to wallet
Account Verified
SecurityCode Verified
Wallet balance added successfully
Sending wallet credit notification
Make ledger entry for accountId abc with txnType credit for amount 10
Starting debit money from wallet
Account Verified
SecurityCode Verified
Wallet balance is Sufficient
Sending wallet debit notification
Make ledger entry for accountId abc with txnType debit for amount 5