Patrones de diseño: comando y conserje en la vida y Ruby

La definición del patrón de comando es estresante de mirar. La definición formal es que:

  • encapsula una solicitud como un objeto
  • lo que le permite parametrizar otros objetos con diferentes solicitudes, solicitudes de colas o registros y soporte de operaciones que se pueden deshacer.

Olvidemos por un segundo y haga un viaje a Hawaii.

Y vive en un hotel de lujo.

Pasamos el día en la playa, buceamos e hicimos un poco de turismo. Es hora de volver al hotel para descansar, comer y planificar para el día siguiente.

Después de regresar al hotel, queremos:

  1. Obtener servicio de habitación para la cena
  2. Obtén servicio de lavandería porque no trajimos ropa extra
  3. Obtenga una guía de viaje para Kauai, la isla a la que vamos mañana

Revisamos el menú de servicio del hotel y encontramos tres artículos de servicio que coinciden con nuestras necesidades.

Luego llamamos a la recepción para hacer estas tres solicitudes. Un conserje recoge nuestra llamada, escribe nuestra lista de solicitudes y actúa sobre cada solicitud de servicio según lo indicado por el menú de servicio.

Luego, cada miembro del personal se ejecuta de acuerdo con cada solicitud específica:

  1. El chef en la cocina comienza a cocinar
  2. El departamento de limpieza envía un personal a nuestra habitación para recoger nuestra ropa
  3. El personal en el lobby toma una guía de viaje y la lleva a nuestra habitación

Repasemos lo que acaba de pasar.

a. Seleccionamos los servicios que queríamos del menú y los enviamos a un conserje.

segundo. El conserje escribió estas solicitudes de servicio como una lista.

do. Después de colgar, instruido por el menú de servicio, el conserje envió nuestras solicitudes a los departamentos correspondientes.

re. Cada departamento ejecutado en la solicitud dada.

Veamos las acciones en Ruby.

1. Presentamos estas tres solicitudes al conserje:

2. Estas solicitudes entraron en una lista que el conserje realiza un seguimiento de:

Veamos eso en acción (consola):

Como podemos ver, después de we presentó tres solicitudes, estas solicitudes estaban en un request_list teniendo cuidado de concierge .

3. Instruido por el menú de servicio, el conserje envió nuestras solicitudes a los departamentos correspondientes.

El código anterior debería funcionar bien.

Excepto por una cosa….

Huele mal

Específicamente, la parte donde tenemos los casos de cambio:

¿Por qué esta parte huele mal?

  1. Si el hotel ofrece veinte servicios, en lugar de tres, el método será realmente largo.
  2. Queremos ofrecer nuevos servicios o eliminar un servicio existente. Sin embargo, cada vez que tenemos que abrir la clase de Concierge y redefinir el método act_on_requests .

El método sabe demasiado y requiere cambios frecuentes . Tener estas dos combinaciones juntas casi siempre es algo malo.

¿Por qué?

Un método que requiere cambios frecuentes es un método que necesita actualizar frecuentemente. Cada vez que actualice un fragmento de código, tendrá la oportunidad de introducir nuevos errores en el sistema.

Cuando el método también conoce una tonelada, y es larga, las posibilidades de estropearlo al actualizar aumentan significativamente. Ese es el razonamiento detrás de un principio de diseño del que hablamos antes : encapsular lo que varía.

¡Hora de refactorizar!

Debe haber una mejor manera que esto:

Eche un vistazo más de cerca y piénselo.

Vamos a reformular lo que el código está haciendo en inglés. Recorrimos las solicitudes en la lista de solicitudes. Para cada solicitud, de acuerdo con su tipo de servicio, proporcionamos los datos correspondientes del departamento y ejecutamos la solicitud. Esencialmente, revisamos las solicitudes y ejecutamos cada una de ellas en consecuencia.

Pero, ¿qué pasa si cada solicitud realmente sabe cómo ejecutarse?

Entonces el método puede ser tan simple como:

En lugar de dejar que el método act_on_requests decida cómo se debe manejar cada solicitud, distribuimos esa responsabilidad y conocimiento a cada solicitud y le act_on_requests decidir cómo manejarla.

Con eso dicho, nuestras solicitudes podrían verse así:

Y el Concierge actualizado se verá así:

Con los códigos actualizados, así es como nosotros, los clientes del hotel, enviamos solicitudes al conserje.

Es bastante fácil crear otro servicio.

Por ejemplo, el hotel también nos permite reservar SPA:

El servicio no solo permite execute (hacer una reserva de spa) sino también undo (cancelar la reserva).

Digamos que el hotel también ofrece otra forma de solicitar servicios sin tener que llamar al conserje, un panel que solicita el servicio:

Podemos presionar el botón y el servicio con una configuración predeterminada se enviará a nuestra habitación.

Aquí está el código para el ServicePanel :

Y aquí es cómo podemos crear un panel de servicio:

?? ¡Ahora estamos usando el patrón de comando! ??

Revisemos la definición del patrón de comando. Eso:

  • encapsula una solicitud como un objeto
  • lo que le permite parametrizar otros objetos con diferentes solicitudes, solicitudes de colas o registros y soporte de operaciones que se pueden deshacer.

1. "encapsula una solicitud como un objeto"

Cada una de las clases de servicios que creamos, RoomService , LaundryService , TripPlanningService y SpaReservationService , es un ejemplo de cómo encapsular una solicitud como un objeto.

Resumen:

2. "lo que le permite parametrizar otros objetos con diferentes solicitudes"

El ServicePanel es un ejemplo de parametrización de un objeto con diferentes solicitudes.

Resumen:

3. "solicitudes de cola o registro"

Nuestras solicitudes fueron puestas en cola mientras el conserje las estaba tomando por teléfono.

Resumen:

4. y soporte de operaciones intercambiables.

SpaReservationService compatible con undo .

Resumen:

¡Gracias por leer!

No te olvides de suscribirte. :RE

Esto fue publicado originalmente en mi blog, Patrones de diseño en la vida y Ruby .