Patrones de diseño con Flutter: 9— Facade
También llamado Fachada por algunos.
Alguna vez llamamos a una operadora, ya sea para telefonía o pedir comida. El problema radica cuando queremos que nos pasen a otro departamento sofisticado, ya sea de entrega o con quien se encarga del pago, vemos que no lo hacen correctamente. Como resultado una pésima experiencia ¿Entonces como podríamos arreglar la comunicación de un servicio a otro?
Se te puede presentar que te unes a un proyecto donde trabajan con una sofisticada gama de librerías y de repente, esto a gran escala. El problema se presenta cuando debes inicializar todos los objetos para llevarlos a un registro de las dependencias. Todo esto en el orden correcto.
El resultado será que nuestra logica del negocio se vera estrechamente acoplada a la implementación de nuestras clases. En otras palabras un código dificil de comprender y mantener.
¿Que podemos hacer con el Patrón Facade?
Facade es un patrón de diseño estructural que proporciona una interfaz simplificada a una biblioteca, un framework o cualquier otro grupo complejo de clases.
Entonces eso es tener una Fachada, que interesante. Verdad?
Tener una fachada resulta útil cuando tienes que integrar tu aplicación con una biblioteca sofisticada con decenas de funciones, de la cual sólo necesitas una pequeña parte.
Clase Simlple(para controlar el estado): SmartHomeState
class SmartHomeState {
bool tvOn = false;
bool audioSystemOn = false;
bool netflixConnected = false;
bool gamingConsoleOn = false;
bool streamingCameraOn = false;
bool lightsOn = true;
}
Facade: SmartHomeFacade
class SmartHomeFacade {
const SmartHomeFacade({
this.gamingFacade = const GamingFacade(),
this.tvApi = const TvApi(),
this.audioApi = const AudioApi(),
this.netflixApi = const NetflixApi(),
this.smartHomeApi = const SmartHomeApi(),
});
final GamingFacade gamingFacade;
final TvApi tvApi;
final AudioApi audioApi;
final NetflixApi netflixApi;
final SmartHomeApi smartHomeApi;
void startMovie(SmartHomeState smartHomeState, String movieTitle) {
smartHomeState.lightsOn = smartHomeApi.turnLightsOff();
smartHomeState.tvOn = tvApi.turnOn();
smartHomeState.audioSystemOn = audioApi.turnSpeakersOn();
smartHomeState.netflixConnected = netflixApi.connect();
netflixApi.play(movieTitle);
}
void stopMovie(SmartHomeState smartHomeState) {
smartHomeState.netflixConnected = netflixApi.disconnect();
smartHomeState.tvOn = tvApi.turnOff();
smartHomeState.audioSystemOn = audioApi.turnSpeakersOff();
smartHomeState.lightsOn = smartHomeApi.turnLightsOn();
}
void startGaming(SmartHomeState smartHomeState) {
smartHomeState.lightsOn = smartHomeApi.turnLightsOff();
smartHomeState.tvOn = tvApi.turnOn();
gamingFacade.startGaming(smartHomeState);
}
void stopGaming(SmartHomeState smartHomeState) {
gamingFacade.stopGaming(smartHomeState);
smartHomeState.tvOn = tvApi.turnOff();
smartHomeState.lightsOn = smartHomeApi.turnLightsOn();
}
void startStreaming(SmartHomeState smartHomeState) {
smartHomeState.lightsOn = smartHomeApi.turnLightsOn();
smartHomeState.tvOn = tvApi.turnOn();
gamingFacade.startStreaming(smartHomeState);
}
void stopStreaming(SmartHomeState smartHomeState) {
gamingFacade.stopStreaming(smartHomeState);
smartHomeState.tvOn = tvApi.turnOff();
smartHomeState.lightsOn = smartHomeApi.turnLightsOn();
}
}
Additional Facade:
Subsystem Clases: AudioApi, CameraApi, PlayStationApi, TvApi, NetflixApi, SmartHomeApi
// AudioApi
class AudioApi {
const AudioApi();
bool turnSpeakersOn() => true;
bool turnSpeakersOff() => false;
}
// CameraApi
class CameraApi {
const CameraApi();
bool turnCameraOn() => true;
bool turnCameraOff() => false;
}
// PlayStationApi
class PlaystationApi {
const PlaystationApi();
bool turnOn() => true;
bool turnOff() => false;
}
// TvApi
class TvApi {
const TvApi();
bool turnOn() => true;
bool turnOff() => false;
}
// NetflixApi
class NetflixApi {
const NetflixApi();
bool connect() => true;
bool disconnect() => false;
void play(String title) {
// ignore: avoid_print
print("'$title' has started started playing on Netflix.");
}
}
// SmartHomeApi - luces inteligentes ON/OFF
class SmartHomeApi {
const SmartHomeApi();
bool turnLightsOn() => true;
bool turnLightsOff() => false;
}
Facade B: GamingFacade
class GamingFacade {
const GamingFacade({
this.playstationApi = const PlaystationApi(),
this.cameraApi = const CameraApi(),
});
final PlaystationApi playstationApi;
final CameraApi cameraApi;
void startGaming(SmartHomeState smartHomeState) {
smartHomeState.gamingConsoleOn = playstationApi.turnOn();
}
void stopGaming(SmartHomeState smartHomeState) {
smartHomeState.gamingConsoleOn = playstationApi.turnOff();
}
void startStreaming(SmartHomeState smartHomeState) {
smartHomeState.streamingCameraOn = cameraApi.turnCameraOn();
startGaming(smartHomeState);
}
void stopStreaming(SmartHomeState smartHomeState) {
smartHomeState.streamingCameraOn = cameraApi.turnCameraOff();
stopGaming(smartHomeState);
}
}
Client: Código Cliente
DeviceIcon
Widget para mostrar el estado actual de cierto servicio a traves de un icono.
import 'package:flutter/material.dart';
class DeviceIcon extends StatelessWidget {
final IconData iconData;
final bool activated;
const DeviceIcon({
required this.iconData,
required this.activated,
});
@override
Widget build(BuildContext context) {
return Icon(
iconData,
color: activated ? Colors.green : Colors.red,
size: 42.0,
);
}
}
ModeSwitcher
Widget componente para cambiar el estado de un servicio a traves de un switch.
import 'package:flutter/material.dart';
class ModeSwitcher extends StatelessWidget {
final String title;
final bool activated;
final ValueSetter<bool>? onChanged;
const ModeSwitcher({
required this.title,
required this.activated,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return SwitchListTile.adaptive(
title: Text(title),
activeColor: Colors.black,
value: activated,
onChanged: onChanged,
);
}
}
FacadeExample
Widget de ejemplo para mostrar el estado de todos los servicios con el “facade” actual.
class FacadeExample extends StatefulWidget {
const FacadeExample();
@override
_FacadeExampleState createState() => _FacadeExampleState();
}
class _FacadeExampleState extends State<FacadeExample> {
final _smartHomeFacade = const SmartHomeFacade();
final _smartHomeState = SmartHomeState();
var _homeCinemaModeOn = false;
var _gamingModeOn = false;
var _streamingModeOn = false;
bool get _isAnyModeOn =>
_homeCinemaModeOn || _gamingModeOn || _streamingModeOn;
void _changeHomeCinemaMode(bool activated) {
if (activated) {
_smartHomeFacade.startMovie(_smartHomeState, 'Movie title');
} else {
_smartHomeFacade.stopMovie(_smartHomeState);
}
setState(() => _homeCinemaModeOn = activated);
}
void _changeGamingMode(bool activated) {
if (activated) {
_smartHomeFacade.startGaming(_smartHomeState);
} else {
_smartHomeFacade.stopGaming(_smartHomeState);
}
setState(() => _gamingModeOn = activated);
}
void _changeStreamingMode(bool activated) {
if (activated) {
_smartHomeFacade.startStreaming(_smartHomeState);
} else {
_smartHomeFacade.stopStreaming(_smartHomeState);
}
setState(() => _streamingModeOn = activated);
}
@override
Widget build(BuildContext context) {
return ScrollConfiguration(
behavior: const ScrollBehavior(),
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(
horizontal: LayoutConstants.paddingL,
),
child: Column(
children: <Widget>[
ModeSwitcher(
title: 'Home cinema mode',
activated: _homeCinemaModeOn,
onChanged: !_isAnyModeOn || _homeCinemaModeOn
? _changeHomeCinemaMode
: null,
),
ModeSwitcher(
title: 'Gaming mode',
activated: _gamingModeOn,
onChanged:
!_isAnyModeOn || _gamingModeOn ? _changeGamingMode : null,
),
ModeSwitcher(
title: 'Streaming mode',
activated: _streamingModeOn,
onChanged: !_isAnyModeOn || _streamingModeOn
? _changeStreamingMode
: null,
),
const SizedBox(height: LayoutConstants.spaceXL * 2),
Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
DeviceIcon(
iconData: FontAwesomeIcons.tv,
activated: _smartHomeState.tvOn,
),
DeviceIcon(
iconData: FontAwesomeIcons.film,
activated: _smartHomeState.netflixConnected,
),
DeviceIcon(
iconData: Icons.speaker,
activated: _smartHomeState.audioSystemOn,
),
],
),
const SizedBox(height: LayoutConstants.spaceXL),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
DeviceIcon(
iconData: FontAwesomeIcons.playstation,
activated: _smartHomeState.gamingConsoleOn,
),
DeviceIcon(
iconData: FontAwesomeIcons.video,
activated: _smartHomeState.streamingCameraOn,
),
DeviceIcon(
iconData: FontAwesomeIcons.lightbulb,
activated: _smartHomeState.lightsOn,
),
],
),
],
),
],
),
),
);
}
}
y YA!
Creacionales:
Estructurales:
De Comportamiento:
- Pronto…..
Tu contribución
👏 ¡Presiona el botón de aplaudir a continuación para mostrar tu apoyo y motivarme a escribir mejor!
💬 Deje una respuesta a este artículo brindando sus ideas, comentarios o deseos para la serie.
📢 Comparte este artículo con tus amigos y colegas en las redes sociales.
➕ Sígueme en Medium.
⭐ Ve los ejemplos prácticos desde mi canal en Youtube: Pacha Code