¿Existe un patrón de diseño que se aplicaría a los modelos de descuento?

35

¿Existen patrones de diseño conocidos para implementar modelos de descuento?

Por modelos de descuento, me refiero a lo siguiente:

  1. Si un cliente compra el Producto X, el Producto Y y el Producto Z, obtiene un descuento del 10% o $ 100.

  2. Si un cliente compra 100 unidades de Producto X, obtiene un descuento del 15% o $ 500

  3. Si un cliente ha traído en el último año más de $ 100K, obtiene un descuento fijo del 20%

  4. Si un cliente ha comprado 2 unidades de Producto X, obtiene 1 unidad de Producto X (o Producto Y) gratis.

  5. ...

¿Existe un patrón genérico que se pueda aplicar para manejar todos los escenarios anteriores? Estoy pensando en algunos modelos, pero no puedo encontrar uno genérico.

Kanini
fuente
IIRC todos los tutoriales que he visto con ejemplos que incluyen descuentos (hay muchos) sugieren un patrón de estrategia
mosquito
2
@ Kanini ¿Es este un problema del mundo real? En tal caso, ¿es este sistema en tiempo real o diferido? ¿Se presentan estas reglas como un motor de reglas o valores de base de datos? ¿La búsqueda de descuentos es jerárquica en función de la prioridad? El patrón de estrategia funcionaría en la mayoría de los casos, pero sus reglas deben tenerse en cuenta para que funcione
Ubermensch
3
Además, si alguien compra 2 unidades de Producto X, un Producto Y y un Producto Z, ¿obtendría tanto el 10% como un producto adicional X?
Ubermensch
@gnat algunos enlaces a algunos de esos tutoriales por favor.
user16764

Respuestas:

18

Si el problema es que necesita aplicar múltiples descuentos, en determinadas circunstancias, es posible que desee considerar el patrón de la Cadena de responsabilidad .

En pocas palabras, pasa la información que desea procesar al primer procesador y, a partir de ahí, decide si la pasa a otros procesadores antes de devolver el resultado.

De esta manera, puede cambiar la estructura y la secuencia de los procesadores sin cambiar el código de llamada.

pdr
fuente
La cadena de responsabilidad es otra buena. Incluso podría ir acompañado de una estrategia. En un caso en el que solo se puede aplicar un descuento, cada estrategia se encadena con otra. Cada cadena calcula su descuento (si el cliente es elegible), lo compara con el descuento anterior y pasa los datos del cliente, el pedido y el descuento a la siguiente cadena. +1.
Thomas Owens
1
Solo mi opinión, pero creo que es más probable que la "Cadena de responsabilidad" esté sobrediseñada para este caso. Una lista simple de modelos de descuento (si es necesario, con un número de pedido) debería hacerlo. La lista en sí es independiente del cliente y sus pedidos, ya que todos los clientes deben ser tratados por igual. La "cadena de responsabilidad" es más apropiada cuando la lista de modelos de descuento cambia con mucha frecuencia en tiempo de ejecución de una manera altamente dinámica.
Doc Brown
11

Los patrones de estrategia, decorador y estado se destacan para mí como posibles puntos de partida. El estado puede ser particularmente útil para 2 o 3, ya que 2 depende del estado del pedido y 3 depende del estado del cliente dentro de un período de tiempo. Estrategia y Decorador se destacan para los demás, ya que puede usar la estrategia para implementar múltiples algoritmos de cálculo de precio de pedido y decorador para agregar nuevos descuentos al pedido.

Sin embargo, recuerde que los patrones de diseño son solo modelos. Puede que no se aplique un solo patrón, sino más bien un sistema de patrones. También considere realizar modificaciones en los modelos descritos para que se adapten mejor a su solución. Es mejor tener un buen diseño que forzar un patrón donde no necesariamente ayuda solo por el hecho de poder decir que tiene un patrón.

Thomas Owens
fuente
Sin embargo, eso no es realmente lo que pretende hacer el patrón de estado, ¿verdad?
pdr
@pdr No veo por qué no. Pero depende de su implementación. Si su objeto de cliente rastrea descuentos dependientes del cliente, entonces podría haber una operación para devolver los descuentos para los que el cliente es elegible. A medida que el cliente compra cosas, los atributos cambian y la implementación de este método cambia a través del patrón de estado.
Thomas Owens
1
Hmm, entiendo lo que quieres decir. Creo que depende de si el cliente es un objeto semipermanente en la aplicación, o simplemente algo que vive en la base de datos y necesita actualizarse. No está claro a partir de la pregunta, es bastante justo. +1
pdr
3
Según mi experiencia, este tipo de reglas comerciales de descuento se modifican todo el tiempo por departamentos de marketing / ventas volubles. Existe una gran necesidad de hacer que sean manejados por los datos y modificables por el usuario en lugar de ser completamente manejados por el código. ¿Cómo afectaría esto la elección del modelo?
jfrankcarr
@jfrankcarr En mi opinión, no lo haría. Rellenaría los valores para conjuntos de artículos que conducen a un descuento, los porcentajes de descuento, etc., desde algún tipo de configuración. Es como construir dinámicamente las transiciones de mi máquina de estado y los atributos de mis decoradores y estrategias.
Thomas Owens
10

Bueno, diseñaría un modelo de descuento como un par "Precondición" y "Descuento", donde "Precondición" es una clase con métodos

  bool IsFulfilled(Customer c);

o y

  bool IsFulfilled(Customer c, Order o);

y el descuento tiene un método void ApplyTo(Customer c). Esto le brinda la capacidad de combinar cualquier tipo de condición previa con cualquier tipo de descuento (creo que esta es una forma de "patrón de puente").

Si tiene un número fijo de condiciones previas, puede resolver el problema creando subtipos específicos (patrón de estrategia). Sin embargo, cuando se permite que sus condiciones previas sean muy complejas, con declaraciones lógicas como AND, OR y NOT, puede implementar mejor algún tipo de intérprete de reglas para las condiciones. Las reglas pueden ser una cadena de texto sin formato escrita en un simple "lenguaje específico de dominio".

Lo mismo ocurre con la clase "Descuento", puede tener algunos subtipos para diferentes tipos de descuentos, o un enfoque general donde las reglas de descuento se dan en forma de texto, evaluadas por algún intérprete.

Doc Brown
fuente
Mi intuición sugiere que esto podría ser lo que está buscando en el contexto de la pregunta
Ubermensch
4
  • Probablemente necesite una interfaz IDiscount ya que todos los diferentes descuentos son lo mismo, y queremos tratarlos conceptualmente como descuentos genéricos.

  • La clase "pedido de este cliente" probablemente necesite una colección de Descuentos. ¿Lista? ¿Picadillo? ¿Lista enlazada? No me importa todavía. ¡Se aplican descuentos a la compra, no al cliente!

  • Mantenga la construcción de instancias de descuento separada del cliente, el carrito de compras, el historial, etc. Cambiará mucho, como señaló @jfrankcarr.

  • Probablemente una clase diferente para cada descuento, ya que el algoritmo y los parámetros para cada descuento varían enormemente e impredeciblemente.

  • Veo un gran manejo de eventos, ya que el cálculo del descuento responde a los cambios del carrito de compras, y viceversa.

Aplicación de patrón de diseño

  • Veo a strategy pattern. IDiscount es la interfaz para implementar diferentes algoritmos de descuento.
  • Veo a factory. Ciertamente, no una abstract factory patternclase completa , sino una sola clase en este punto del análisis. Razonablemente, debe haber un solo lugar donde haya suficiente contexto para decidir qué descuentos se aplican y luego crearlos. Una clase. Si las reglas para aplicar los descuentos explotan más tarde debido a una seta del Departamento de Marketing, cualquier lógica adicional de Construcción de descuentos aún debe fusionarse en esta clase base de fábrica, creo.

  • Puedo ver Chain of Responsibility. Esto no es mutuamente exclusivo de la factoryidea. En lugar de repetir una colección de descuentos, cada descuento llama al siguiente tipo. La clase "pedido del cliente" no tiene una colección de descuentos en este caso.

  • El factor "hmmmm ..." en la Cadena de responsabilidad, creo, es que cada descuento tiene una referencia a la siguiente. La implicación es que el orden importa. Que no es el caso. Además, el concepto que representa el CDR es que un objeto no puede manejar la solicitud, por lo que se pasa "a la siguiente autoridad superior". Nuestro modelo es diferente. La única solicitud es calcular. Todo descuento hace esto. La salida o el efecto puede ser nulo pero se calcula cada descuento. Me inclino instintivamente hacia una mayor fidelidad en el mundo real.

Supuestos

  • Los descuentos se basan en el carrito de compras actual y / o el historial de compras.
  • Se pueden aplicar cero o más descuentos. No hay descuentos mutuamente excluyentes.
  • El cálculo adecuado no depende del orden en que se aplican los descuentos.

¿Qué cambia, qué permanece igual?

  • Los descuentos son muy diferentes. Diferentes números y tipos de parámetros para formar cada regla.

  • Los argumentos para los descuentos que califican cambian a medida que cambia el carrito de compras.

  • El número de descuentos disponibles cambia

  • Los descuentos que este cliente califica para cambios a medida que cambia su carrito de compras.

  • El historial de compras no cambia en el contexto de esta compra

  • El costo total cambia dinámicamente en función de las líneas de compra y los descuentos aplicados.

  • Después de la aplicación inicial, la salida de un descuento puede cambiar, ya que la cantidad de compra cambia, por ejemplo.

radarbob
fuente
Excelente y completa respuesta ... PERO solo tengo una preocupación y es que la sección de supuestos no debería estar allí, al menos para no liderar la respuesta. La idea general es que el patrón ayuda a brindar exactamente comodidad y olvidarse de los "supuestos", la regla genérica no necesita saber cómo se realizan los cálculos y qué utiliza cualquier implementación detallada del contexto (Cliente, Artículos del carrito , Hora del día, temporada, etc.). Realmente ayuda completa sin embargo
le0diaz
Las viñetas iniciales y la sección 'Suposiciones' son solo mi razonamiento de "muestra tu trabajo" sobre el problema en sí, que por supuesto tiene una influencia en el diseño del modelo de descuento. Por ejemplo, mis suposiciones sobre la orden de ejecución de descuento y la interdependencia me llevan a dejar de enfatizar la Cadena de responsabilidad. Tenga en cuenta que estoy pensando en la intención del patrón, no en la complejidad como lo hace @docbrown. Soy un defensor exuberante para reflejar la intención en el diseño.
radarbob
1

Lógicamente, un modelo de descuento puede ser cualquier cosa , por lo que no puede suponer que puede programar todos los casos por adelantado. Tampoco nadie que responda su pregunta puede estar completamente seguro de lo que realmente necesita. Sin embargo, suponiendo que obtenga los tipos habituales de descuentos que se encuentran en el mundo real ...

Una gran pregunta es si los descuentos se programarán o si desea que los usuarios los ingresen. Como se mencionó anteriormente, nunca se puede programar, pero generalmente el objetivo es tratar de hacer que ingrese más datos como en casos comunes, en lugar de programarlos a todos. Esto se aplica en cierta medida, incluso si los programadores se utilizan para crear todos los descuentos.

Martin Fowler menciona "Método de instancia individual" en "Patrones de análisis: modelos de objetos reutilizables" como parte de cómo implementar "Reglas de contabilización" para sistemas de contabilidad, pero las reglas parecen bastante similares a las suyas. Daría más detalles pero es un trabajo protegido por derechos de autor y

Para una interfaz de usuario, debe encontrar casos de uso que sean bastante simples o crear un intérprete y un generador de consultas. Posiblemente ambos, uno para casos simples y otro más avanzado. Si escribe un intérprete, es probable que este sea un caso bastante bueno para usar el patrón Intérprete, ya que es relativamente simple de codificar en comparación con un generador de analizador, y el tiempo de análisis más lento probablemente no importará realmente. (Si te gusta el uso de generadores de analizadores sintácticos no dejar que te detenga).

No trate de hacer todo con un intérprete, aunque - en algún momento usted está programando en su propio idioma de mala muerte, por lo que también podría utilizar una de verdad. Si su lenguaje interpretado es compatible con funciones (que probablemente debería apoyar llamándolos - definirlos es dudosa) las que se pueden codificar en un lenguaje real. No ir más lejos por este camino que tiene que hacerlo.

No importa lo que hagas, eventualmente alguien va a querer que el descuento se basará en si ha comprado un plazo de 30 días hábiles de una promoción - donde cuentan días laborales solamente si no hay vacaciones en la región definida por cualquiera código postal de la tienda o las del cliente código postal. Así que no se trata de diseñar el sistema perfecto de antemano - asuma que ocasiones deberá escribir el código para los nuevos tipos de descuentos y diseño en consecuencia.

psr
fuente
0

Es que no hay ningún punto está preguntando si hay un patrón útil para esto? ¿Qué tipo de patrón se requiere: estructural o de comportamiento?

Idealmente, si tuviera que escribir un software para esto, todo lo que se necesita es un algoritmo . Una función simple que calcula el descuento total de la siguiente manera:

cart.calculateDiscount(productVector);

¡No necesitas nada más que esto!

Para aclarar: entiendo que habrá muchas reglas: la más básica de dicha representación debe tener la forma de una base de reglas (conjunto de atributos de datos y el descuento resultante contra ella) y la función como la anterior la iteraría para calcularla. Si las reglas se agregan o eliminan, no debería terminar cambiando el código, solo cambie la base de la regla.

Se requerirá un patrón solo si necesitamos diferentes objetos para acceder a las API de cada uno o comunicarnos para establecer una tarea.

PD: Piénselo: cuando el cortafuegos procesa paquetes y los pasa o los rechaza (o los modifica), ¿qué patrón de diseño utiliza? ¡La respuesta es NINGUNA de las descritas anteriormente!

Dipan Mehta
fuente
¡Por supuesto que necesitas más que eso! La idea es que el algoritmo no esté estrechamente relacionado con la implementación del código. Si verifica los escenarios es muy probable que surjan más escenarios, también lo mencionó de alguna manera, realmente no sabe de qué dependerá ninguna otra 'Regla'. Es ingenuo pensar que una regla solo dependerá de la lista de productos, pero en realidad depende del cliente, el tiempo, la temporada, etc. No sé qué implementación de Firewall comprobó, pero las que he verificado TIENEN muchos patrones estructurales / de diseño
le0diaz