Usando un patrón de estrategia y un patrón de comando


121

Ambos patrones de diseño encapsulan un algoritmo y desacoplan los detalles de implementación de sus clases de llamada. La única diferencia que puedo discernir es que el patrón de estrategia toma parámetros para la ejecución, mientras que el patrón de comando no.

Me parece que el patrón de comando requiere que toda la información para la ejecución esté disponible cuando se crea, y puede retrasar su llamada (quizás como parte de un script).

¿Qué determinaciones guían si utilizar un patrón u otro?

Respuestas:


94

Incluyo una tabla de jerarquía de encapsulación de varios de los patrones de diseño de GoF para ayudar a explicar las diferencias entre estos dos patrones. Con suerte, ilustra mejor lo que encapsula cada uno para que mi explicación tenga más sentido.

En primer lugar, la jerarquía enumera el alcance para el que se aplica un patrón determinado, o el patrón apropiado a utilizar para encapsular algún nivel de detalle, según el lado de la tabla en el que comience.

tabla de jerarquía de encapsulación de patrones de diseño

Como puede ver en la tabla, un objeto de Patrón de estrategia oculta los detalles de la implementación de un algoritmo, por lo que el uso de un objeto de estrategia diferente realizará la misma funcionalidad pero de una manera diferente. Cada objeto de estrategia puede optimizarse para un factor particular u operar sobre algún otro parámetro; y, mediante el uso de una interfaz común, el contexto puede funcionar de forma segura con cualquiera de ellos.

El patrón de comando encapsula un nivel de detalle mucho menor que un algoritmo. Codifica los detalles necesarios para enviar un mensaje a un objeto: receptor, selector y argumentos. El beneficio de objetivar una parte tan pequeña de la ejecución del proceso es que dichos mensajes se pueden invocar a lo largo de diferentes puntos de tiempo o ubicación de una manera general sin tener que codificar sus detalles. Permite que los mensajes se invoquen una o más veces, o que se pasen a diferentes partes del sistema o múltiples sistemas sin requerir que se conozcan los detalles de una invocación específica antes de la ejecución.

Como es típico para los patrones de diseño, no requieren que todas las implementaciones sean idénticas en detalle para llevar el nombre del patrón. Los detalles pueden variar en la implementación y en qué datos se codifican en el objeto en comparación con los argumentos del método.


Entonces, si tuviera un sistema que filtrara resultados con una "tubería de filtro" y usara delegados como filtros (donde cada uno de los algoritmos del filtro se encapsularía dentro de una función), ¿se consideraría un patrón de comando? En este caso, veo que el delegado de la función de filtro proporciona una especie de contrato para lo que cada filtro debe cumplir en términos de entrada y salida.
KTF

4
@KTF, no. El patrón de comando emplea un objeto que tiene la mayoría (si no toda) la información necesaria (por ejemplo, receptor, selector, argumentos) para invocar el método de un objeto. Es un patrón simplista que se puede utilizar en otros patrones de diseño, como Cadena de responsabilidad, Colección y el patrón Pipeline que describe. El "contrato de clases" proporcionado por sus delegados es otro patrón, Interfaz.
Huperniketes

50

Las estrategias encapsulan algoritmos. Los comandos separan al remitente del receptor de una solicitud, convierten una solicitud en un objeto.

Si es un algoritmo, cómo se hará algo, use una estrategia. Si necesita separar la llamada de un método de su ejecución, use un comando. Los comandos se utilizan a menudo cuando pone en cola mensajes para su uso posterior, como una tarea o una transacción.


eso tenía sentido en.wikipedia.org/wiki/Command_Pattern cliente e invocador están empatados, pero al mismo tiempo, ¡no se conocen el uno al otro!
Kalpesh Soni

26

Respondiendo a una pregunta muy antigua. (¿Alguien ve las últimas respuestas en lugar de las más votadas?)

Es una confusión válida debido a las similitudes. Tanto los patrones de estrategia como los de comando utilizan encapsulación . Pero eso no los hace iguales.

La diferencia clave es comprender qué está encapsulado. El principio de OO, del que dependen ambos patrones, es Encapsular lo que varía .

En caso de estrategia, lo que varía es el algoritmo . Por ejemplo, un objeto de estrategia sabe cómo exportar a un archivo XML, mientras que el otro lo hace, digamos, JSON. Los diferentes algoritmos se mantienen ( encapsulados ) en diferentes clases. Es tan simple como eso.

En caso de comando, lo que varía es la propia solicitud . La solicitud puede provenir de File Menu > Deleteo Right Click > Context Menu > Deleteo Just Delete Button pressed. Los tres casos pueden generar 3 objetos de comando del mismo tipo. Estos objetos de comando solo representan 3 solicitudes de eliminación; no algoritmo de eliminación. Dado que las solicitudes ahora son un montón de objetos, podríamos administrarlas fácilmente. De repente, se vuelve trivial proporcionar funciones como deshacer o rehacer.

No importa cómo el comando implemente la lógica solicitada. Al llamar a execute (), puede implementar un algoritmo para activar la eliminación o incluso puede delegarlo a otros objetos, incluso puede delegarlo en una estrategia. Es solo un detalle de implementación del patrón de comando. Es por eso que se nombra como comando, aunque no es una forma educada de solicitar : -)

Compárelo con la estrategia; este patrón solo se ocupa de la lógica real que se ejecuta. Si hacemos eso, ayuda a lograr diferentes combinaciones de comportamientos con un conjunto mínimo de clases, evitando así la explosión de clases.

Creo que Command nos ayuda a ampliar nuestra comprensión de la encapsulación, mientras que Strategy proporciona un uso natural de la encapsulación y el polimorfismo.


15

La forma en que lo veo es que tiene varias formas de hacer lo mismo, cada una de ellas es una estrategia, y algo en tiempo de ejecución determina qué estrategia se ejecuta.

Tal vez primero pruebe StrategyOne, si los resultados no son lo suficientemente buenos, prueba StrategyTwo ...

Los comandos están vinculados a distintas cosas que deben suceder, como TryToWalkAcrossTheRoomCommand. Este comando se activará siempre que algún objeto intente cruzar la habitación, pero dentro de él, podría probar StrategyOne y StrategyTwo para intentar cruzar la habitación.

marca


2
RE: "múltiples formas de hacer lo mismo" - Eso parece entrar en conflicto con algunos de los ejemplos comunes de Estrategia. Específicamente aquellos donde hay clases de implementación que hacen sumas, restas, multiplicaciones, etc. ¿Quizás esos no son buenos ejemplos?
Joshua Davis

1
@JoshuaDavis todas estas "subestrategias" son casos especiales de una estrategia: operación aritmética . tienen argumentos comunes (2 operandos) y producen un valor como resultado. prácticamente haciendo lo mismo (siendo cajas negras) a su manera diferente, dependiendo de la implementación. así que no veo ningún conflicto aquí, sino todo lo contrario: buen ejemplo =)
jungle_mole

7

Puede que me equivoque en mi opinión, pero trato el comando como una función a ejecutar o una reacción. Debe haber al menos dos jugadores: el que solicita la acción y el que ejecuta la acción. La GUI es un ejemplo típico de patrón de comando:

  • Todos los botones de la barra de herramientas de la aplicación están asociados con alguna acción.
  • Button es el ejecutor en este caso.
  • La acción es el comando en este caso.

El comando generalmente está limitado a algún ámbito o área comercial, pero no es necesario: puede tener comandos que emitan una factura, inicien un cohete o eliminen un archivo implementando la misma interfaz (por ejemplo, execute()método único ) dentro de una aplicación. A menudo, los comandos son autocontenidos, por lo que no necesitan nada del ejecutor para procesar la tarea que pretenden realizar (toda la información necesaria se proporciona en el momento de la construcción), a veces los comandos son sensibles al contexto y deberían poder descubrir este contexto ( Retroceso de comandos debe conocer la posición de intercalación en el texto para eliminar correctamente el carácter anterior; rollback comando debe descubrir la transacción actual de reversión; ...).

La estrategia es un poco diferente: está más ligada a un área. La estrategia puede definir una regla para formatear una fecha (¿en UTC? ¿Específico de la localidad?) (Estrategia de "formateador de fechas") o para calcular un cuadrado para una figura geométrica (estrategia de "calculadora de cuadrados"). Las estrategias son en este sentido objetos de peso mosca, que toman algo como entrada ("fecha", "figura", ...) y toman alguna decisión en base a ello. Quizás no sea el mejor, pero buen ejemplo de estrategia es uno conectado con javax.xml.transform.Sourcela interfaz: dependiendo de si el objeto pasado es DOMSourceo SAXSourceo StreamSourcela estrategia (= XSLT transformador en este caso) se aplicarán diferentes reglas para procesarlo. La implementación puede ser simple switcho involucrar un patrón de Cadena de responsabilidad .

Pero, de hecho, hay algo en común entre estos dos patrones: los comandos y las estrategias encapsulan algoritmos dentro de la misma área semántica.


1
Trato el comando como una función de devolución de llamada o una reacción. Debería haber al menos dos jugadores: uno que solicita la acción y otro que ejecuta ... - Entiendo lo que estás tratando de decir, pero no usaría la palabra 'devolución de llamada', porque a menudo la palabra 'callback' implica una invocación asincrónica y no es necesario realizar invocaciones asincrónicas para que el patrón de comando sea útil. Caso en cuestión: Microsoft Word. Los clics en los botones de la barra de herramientas y las teclas de método abreviado no invocan comandos asincrónicos, pero podemos apreciar cómo el patrón de comando sería útil en este caso
Jim G.

Estoy de acuerdo, aunque como dijo Jim, editaría para eliminar la referencia a la devolución de llamada.
JARC

Gracias, he hecho algunas extensiones. Avísame si estás de acuerdo o en desacuerdo.
dma_k

5

Mando:

Componentes básicos:

  1. Command declara una interfaz para comandos abstractos comoexecute()
  2. El receptor sabe cómo ejecutar un comando en particular
  3. Invoker tiene ConcreteCommand , que debe ejecutarse
  4. El cliente crea ConcreteCommand y asigna un receptor
  5. ConcreteCommand define el enlace entre Command y Receiver

Flujo de trabajo:

El cliente llama a Invoker => El invocador llama a ConcreteCommand => ConcreteCommand llama al método Receiver , que implementa el método de comando abstracto .

Ventaja : el cliente no se ve afectado por los cambios en Command y Receiver. El invocador proporciona un acoplamiento flexible entre el cliente y el receptor. Puede ejecutar varios comandos con el mismo invocador.

El patrón de comando le permite ejecutar un comando en diferentes receptores usando el mismo invocador . El invocador desconoce el tipo de receptor

Para una mejor comprensión de los conceptos, eche un vistazo a este artículo de JournalDev de Pankaj Kumar y al artículo de dzone de James Sugrue, además del enlace de Wikipedia.

Puede utilizar el patrón Command para

  1. Desacoplar al invocador y al receptor del mando

  2. Implementar mecanismo de devolución de llamada

  3. Implementar la funcionalidad de deshacer y rehacer

  4. Mantener un historial de comandos

java.lang.Threades una buena implementación del patrón Command . Puede tratar Thread como invocador y clase implementando Runnable como ConcreteCommonad / Receiver y el run()método como Command .

La versión Deshacer / Rehacer del patrón de comando se puede leer en el artículo de Theodore Norvell

Estrategia:

El patrón de estrategia es muy simple de entender. Utilice este patrón cuando

Tiene varias implementaciones para un algoritmo y la implementación del algoritmo puede cambiar en el tiempo de ejecución según las condiciones particulares .

Tome un ejemplo del componente de tarifa del sistema de reserva de aerolíneas

A las aerolíneas les gustaría ofrecer diferentes tarifas durante diferentes períodos de tiempo: meses pico y no pico. Durante los días de menor actividad turística, le gustaría estimular la demanda ofreciendo atractivos descuentos.

Conclusiones clave del patrón de estrategia :

  1. Es un patrón de comportamiento
  2. Se basa en la delegación
  3. Cambia las entrañas del objeto modificando el comportamiento del método.
  4. Se usa para cambiar entre familias de algoritmos
  5. Cambia el comportamiento del objeto en tiempo de ejecución.

Publicaciones relacionadas con ejemplos de código:

Usando el patrón Command Design

Ejemplo del mundo real del patrón de estrategia


0

Para mí, la diferencia es de intención. Las implementaciones de ambos patrones son bastante similares, pero tienen diferentes propósitos:

  • Para una estrategia, el componente que usa el objeto sabe lo que hace el objeto (y lo usará para realizar una parte de su propio trabajo), pero no le importa cómo lo hace.

  • Para un comando, el componente que usa el objeto no sabe lo que hace el comando ni cómo lo hace, solo sabe cómo invocarlo. La tarea de la persona que llama es simplemente ejecutar el comando; el procesamiento realizado por el comando no forma parte del trabajo principal de la persona que llama.

Esta es la diferencia: ¿el objeto que usa el componente realmente sabe o se preocupa por lo que hace el componente? La mayoría de las veces, esto se puede determinar en función de si el objeto patrón devuelve un valor a su invocador. Si el invocador se preocupa por lo que hace el objeto patrón, probablemente querrá que devuelva algo y será una estrategia. Si no le importa ningún valor de retorno, probablemente sea un comando (nota, algo como un Java Callable sigue siendo un comando porque, aunque devuelve un valor, a la persona que llama no le importa el valor, simplemente lo devuelve a lo que originalmente suministró el comando).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.