¿Qué es realmente la "codificación suave"?

87

En este artículo de Alex Papadimoulis, puedes ver este fragmento:

private void attachSupplementalDocuments()
{
  if (stateCode == "AZ" || stateCode == "TX") {

    //SR008-04X/I are always required in these states
    attachDocument("SR008-04X");
    attachDocument("SR008-04XI");
  }

  if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

  if (coInsuredCount >= 5  && orgStatusCode != "CORP") {
    //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A
    attachDocument("AUTHCNS-1A");
  }
}

Realmente no entiendo este artículo.

Yo cito:

Si cada constante de la regla de negocio se almacenara en algún archivo de configuración, la vida sería mucho [más ( sic )] difícil para todos los que mantienen el software: habría muchos archivos de código que compartían uno, un archivo grande (o, por el contrario, una gran cantidad de pequeños archivos de configuración); desplegar cambios en las reglas de negocio no requiere un código nuevo, sino cambiar manualmente los archivos de configuración; y la depuración es mucho más difícil.

Este es un argumento en contra de tener el entero constante "500000" en un archivo de configuración, o el "AUTHCNS-1A" y otras constantes de cadena.

¿Cómo puede ser esto una mala práctica?

En este fragmento, "500000" no es un número. No es, por ejemplo, lo mismo que:

int doubleMe(int a) { return a * 2;}

donde 2, es un número que no necesita ser abstraído. Su uso es obvio y no representa algo que pueda reutilizarse más adelante.

Por el contrario, "500000" no es simplemente un número. Es un valor significativo, uno que representa la idea de un punto de interrupción en la funcionalidad. Este número podría usarse en más de un lugar, pero no es el número que está usando; es la idea del límite / límite, por debajo del cual se aplica una regla y por encima del cual se aplica otra.

¿Cómo es referirse a él desde un archivo de configuración, o incluso a #define, consto lo que sea que proporcione su idioma, peor que incluir su valor? Si más adelante el programa, o algún otro programador, también requiere ese límite, para que el software haga otra elección, estás jodido (porque cuando cambia, nada te garantiza que cambiará en ambos archivos). Eso es claramente peor para la depuración.

Además, si mañana el gobierno exige "Desde el 5/3/2050, debe agregar AUTHLDG-122B en lugar de AUTHLDG-1A", esta constante de cadena no es una constante de cadena simple. Es uno que representa una idea; es solo el valor actual de esa idea (que es "lo que agrega si el libro mayor está por encima de 500k").

Déjame aclarar. No digo que el artículo esté equivocado; Simplemente no entiendo; tal vez no esté muy bien explicado (al menos para mi pensamiento).

Entiendo que reemplazar cada posible valor literal o numérico de cadena con una variable constante, definida o de configuración, no solo no es necesario, sino que complica demasiado las cosas, sino que este ejemplo en particular no parece pertenecer a esta categoría. ¿Cómo sabes que no lo necesitarás más adelante? ¿O alguien más para el caso?

K. Gkinis
fuente
21
Juega el rompecabezas: ¿cuál sería un buen nombre para esos números? Creo que encontrará que el nombre no agrega ningún valor, o describe todo lo que el código ya describe y, a menudo, agrega ambigüedad ("LedgerLimitForAuthDlg1A"). El artículo me pareció brillante exactamente por lo relevante que es esto. He mantenido sistemas que han utilizado ambos enfoques, y puedo decirle que las reglas comerciales pertenecen al código, lo que las hace mucho más fáciles de rastrear, mantener y comprender. Cuando usa la configuración, es mejor que cuente: es mucho más costoso.
Luaan
2
La configuración debe reservarse para las cosas que deben configurarse. Si las reglas de negocio no son configurables en general, poner partes de ella en la configuración de todos modos no le aporta nada.
biziclop
Para lenguajes adecuadamente avanzados, la configuración toma la forma de subrutinas reales y no cadenas.
Thorbjørn Ravn Andersen

Respuestas:

100

El autor advierte contra la abstracción prematura.

La línea se if (ledgerAmt > 500000)parece al tipo de regla comercial que esperaría ver para sistemas comerciales grandes y complejos cuyos requisitos son increíblemente complejos pero precisos y bien documentados.

Por lo general, ese tipo de requisitos son casos excepcionales / límite en lugar de lógica reutilizable útil. Esos requisitos suelen ser propiedad y mantenidos por analistas comerciales y expertos en la materia, en lugar de ingenieros

(Tenga en cuenta que la `` propiedad '' de los requisitos por parte de los analistas de negocios / expertos en estos casos generalmente ocurre cuando los desarrolladores que trabajan en campos especializados no tienen suficiente experiencia en el dominio; aunque todavía esperaría una comunicación / cooperación completa entre los desarrolladores y los expertos en el dominio para proteger contra requisitos ambiguos o mal escritos).

Cuando se mantienen sistemas cuyos requisitos están llenos de casos límite y lógica altamente compleja, generalmente no hay forma de abstraer esa lógica o hacerla más fácil de mantener; Los intentos de intentar crear abstracciones pueden ser contraproducentes, no solo dando como resultado una pérdida de tiempo, sino también un código menos mantenible.

¿Cómo se refiere a él desde un archivo de configuración, o incluso un #define, const o lo que sea que proporcione su idioma, peor que incluir su valor? Si más adelante el programa, o algún otro programador, también requiere ese límite, para que el software haga otra elección, estás jodido (porque cuando cambia, nada te garantiza que cambiará en ambos archivos). Eso es claramente peor para la depuración.

Este tipo de código tiende a estar protegido por el hecho de que el código en sí mismo probablemente tenga una correspondencia uno a uno con los requisitos; es decir, cuando un desarrollador sabe que la 500000cifra aparece dos veces en los requisitos, ese desarrollador también sabe que aparece dos veces en el código.

Considere el otro escenario (igualmente probable) donde 500000aparece en varios lugares en el documento de requisitos, pero los expertos en la materia deciden cambiar solo uno de ellos; allí tiene un riesgo aún peor de que alguien que cambia el constvalor no se dé cuenta de que 500000se usa para significar cosas diferentes, por lo que el desarrollador lo cambia en el único lugar donde lo encuentra en el código y termina rompiendo algo que ellos no se dio cuenta de que habían cambiado.

Este escenario sucede mucho en software legal / financiero a medida (por ejemplo, lógica de cotización de seguros): las personas que escriben dichos documentos no son ingenieros, y no tienen problemas para copiar + pegar fragmentos enteros de la especificación, modificando algunas palabras / números, pero dejando la mayor parte igual.

En esos escenarios, la mejor manera de lidiar con los requisitos de copiar y pegar es escribir el código de copiar y pegar, y hacer que el código se vea lo más similar posible a los requisitos (incluida la codificación de todos los datos).

La realidad de tales requisitos es que generalmente no permanecen copiando + pegando por mucho tiempo, y los valores a veces cambian de manera regular, pero a menudo no cambian en tándem, por lo que tratar de racionalizar o abstraer esos requisitos o simplificarlos De cualquier forma, terminan creando más dolores de cabeza de mantenimiento que simplemente traducir los requisitos textualmente al código.

Ben Cottrell
fuente
28
Un lenguaje específico de dominio (DSL) puede ser una buena manera de hacer que el código se lea más como el documento de requisitos.
Ian
13
Otra ventaja de un DSL es que también hace que sea más difícil mezclar accidentalmente la aplicación, la presentación o la lógica de persistencia con las reglas comerciales.
Erik Eidt
16
Pensar que su aplicación es lo suficientemente especial como para garantizar su propio DSL es usualmente arrogante.
brian_o
8
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineerslo cual no siempre es una buena idea. A veces, el acto de convertir esos requisitos en código revelará casos de esquina donde los requisitos no están bien definidos o están definidos de tal manera que va en contra del interés comercial. Si los analistas y desarrolladores de negocios pueden cooperar para lograr un objetivo común, entonces se pueden evitar muchos problemas.
kasperd
44
@BenCottrell No estaba sugiriendo cambiar las reglas para que sea más fácil escribir el software. Pero cuando tiene muchas condicionales en las reglas, es completamente posible que se pierda alguna interacción entre ellas al definir las reglas en primer lugar. Pero a medida que convierte la especificación en código, el desarrollador está obligado a notar que existe una posible interacción entre esas condiciones. En este punto, es posible que el desarrollador encuentre que una interpretación estricta de la especificación conduce a un precio involuntario que permitiría a los clientes jugar con el sistema.
kasperd
44

El artículo tiene un buen punto. ¿Cómo puede ser una mala práctica extraer constantes en un archivo de configuración? Puede ser una mala práctica si complica el código innecesariamente. Tener un valor directamente en el código es mucho más simple que tener que leerlo desde un archivo de configuración, y el código tal como está escrito es fácil de seguir.

Además, mañana, el gobierno dice "A partir del 5/3/2050, debe agregar AUTHLDG-122B en lugar de AUTHLDG-1A".

Sí, entonces cambias el código. El objetivo del artículo es que no es más complicado cambiar el código que cambiar un archivo de configuración.

El enfoque descrito en el artículo no se escala si obtienes una lógica más compleja, pero el punto es que tienes que hacer un juicio y, a veces, la solución más simple simplemente es la mejor.

¿Cómo sabes que no lo necesitarás más adelante? ¿O alguien más para el caso?

Este es el punto del principio YAGNI. No diseñe para un futuro desconocido que pueda resultar completamente diferente, diseñe para el presente. Tiene razón en que si el valor 500000 se usa en varios lugares del programa, por supuesto, debe extraerse a una constante. Pero este no es el caso en el código en cuestión.

Softcoding es realmente una cuestión de separación de preocupaciones . La información de softcode que sabe que puede cambiar independientemente de la lógica de la aplicación principal. Nunca codificaría una cadena de conexión a una base de datos, porque sabe que puede cambiar independientemente de la lógica de la aplicación y tendrá que diferenciarla para diferentes entornos. En una aplicación web, nos gusta separar la lógica empresarial de las plantillas html y las hojas de estilo, ya que pueden cambiar de forma independiente e incluso pueden ser cambiadas por diferentes personas.

Pero en el caso de la muestra de código, las cadenas y los números codificados son una parte integral de la lógica de la aplicación. Es concebible que un archivo pueda cambiar su nombre debido a algún cambio de política fuera de su control, pero es igualmente concebible que necesitemos agregar una nueva comprobación if-branch para una condición diferente. Extraer los nombres y números de archivo en realidad rompe la cohesión en este caso.

JacquesB
fuente
44
A menudo es mucho más complicado cambiar el código que un archivo de configuración. Es posible que necesite un desarrollador y un ciclo de lanzamiento / sistema de compilación para el primero, mientras que el último solo requiere cambiar un número en un cuadro en una interfaz de usuario amigable de configuración.
OrangeDog
66
@OrangeDog Sí, así es como se ve al principio. Pero si hace cosas como esta, la interfaz de usuario de configuración será todo menos amigable, con cientos de cuadros de texto totalmente sin sentido que le preguntarán quién sabe qué. Y ahora tiene que construir la interfaz de usuario y documentarla. Eso sí, eso no significa que la configuración nunca sea una buena opción, hay casos en los que es absolutamente la mejor opción. Pero no en ninguno de los ejemplos en el artículo. ¿Y cuándo fue la última vez que una legislación cambió solo el número? La última vez que las reglas del IVA cambiaron aquí, tuvimos que rehacer todos los cálculos de todos modos.
Luaan
2
@OrangeDog: Aquí está asumiendo que la configuración del software le proporciona los ganchos necesarios para la verificación que necesita hacer. ¡Observe cómo en el OP todos y cada uno ifse basan en una variable diferente! Si la variable que necesita no está accesible desde la configuración, debe modificar el software de todos modos.
Matthieu M.
2
@OrangeDog, ¿está sugiriendo que debería haber cambios significativos en la lógica de una aplicación de software, sin un ciclo dev / qa / release y pruebas apropiadas?
NPSF3000
3
@OrangeDog: OK, usa YAML para configurar la lógica en el ejemplo. Como la lógica incluye reglas condicionales, encontrará una manera de representar estos condicionales en YAML. Felicidades, has reinventado Python. ¿Por qué no escribir toda la aplicación en Python entonces?
JacquesB
26

El artículo continúa hablando sobre 'Enterprise Rule Engine's, que probablemente son un mejor ejemplo de lo que está argumentando.

La lógica es que puede generalizar hasta el punto en que su configuración se vuelve tan complicada que contiene su propio lenguaje de programación.

Por ejemplo, el código de estado para la asignación de documentos en el ejemplo podría moverse a un archivo de configuración. Pero entonces necesitarías expresar una relación compleja.

<statecode id="AZ">
    <document id="SR008-04X"/>
    <document id="SR008-04XI"/>
</statecode>

¿Quizás también pondrías la cantidad del libro mayor?

<statecode id="ALL">
    <document id="AUTHLDG-1A" rule="ledgerAmt >= 50000"/>
</statecode>

Pronto descubrirá que está programando en un nuevo lenguaje que ha inventado y guardando ese código en archivos de configuración que no tienen fuente ni control de cambios.

Cabe señalar que este artículo es de 2007 cuando este tipo de cosas era un enfoque común.

Hoy en día probablemente resolveríamos el problema con la inyección de dependencia (DI). Es decir, tendrías un 'código duro'

InvoiceRules_America2007 : InvoiceRules

que reemplazarías con un código rígido o más configurable

InvoiceRules_America2008 : InvoiceRules

cuando la ley o los requisitos comerciales cambiaron.

Ewan
fuente
44
Quizás deberías definir "DI". Y tal vez explique un poco más.
Basil Bourque
99
¿Por qué ese archivo no estaría en el sistema de control de fuente?
JDługosz
2
Si es específico del cliente, ¿la versión codificada tiene un gran desorden de ifdeclaraciones para dar diferentes valores para cada cliente? Eso suena como algo que debería estar en un archivo de configuración. Estar en un tipo de archivo u otro, siendo todo lo demás igual, no es una razón para no controlar / rastrear / respaldar el archivo. @ewan parece estar diciendo que el archivo de un DSL no se puede guardar como parte del proyecto por alguna razón, cuando incluso los activos que no son de código, como las imágenes y los archivos de sonido y la documentación, sí lo son .
JDługosz
2
Realmente deberías refactorizar el valor "50000" de tu XML y ponerlo en un archivo de configuración separado, ¿no crees? ... y se supone que es 500000, por cierto.
Comodín el
1
@jdlugosz, el concepto de ERE es que usted compra el sistema y luego lo configura según sus necesidades. quizás porque los desarrolladores internos competían con estos sistemas 'flexibles' que tratarían de emularlos. cambiar el control de la configuración, incluso en sistemas de grandes compañías como IBM, fue una idea de último momento. El punto de venta fue un cambio rápido
Ewan
17

Por el contrario, "500000" no es simplemente un número. Es un valor significativo, uno que representa la idea de un punto de interrupción en la funcionalidad. Este número podría usarse en más de un lugar, pero no es el número que está usando, es la idea del límite / límite, debajo de cuál se aplica una regla y por encima de cuál otra.

Y eso se expresa al tener (y podría argumentar que incluso el comentario es redundante):

 if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

Esto es solo repetir lo que está haciendo el código:

LEDGER_AMOUNT_REQUIRING_AUTHLDG1A=500000
if (ledgerAmnt >= LEDGER_AMOUNT_REQUIRING_AUTHLDG1A) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
}

Tenga en cuenta que el autor supone que el significado de 500000 está vinculado a esta regla; no es un valor que se pueda reutilizar en otro lugar:

El único cambio en la regla de negocios que esta Soft Coding precedente podría tener en cuenta es un cambio en la cantidad del libro mayor que requería un formulario AUTHLDG-1A. Cualquier otro cambio en las reglas de negocio requeriría aún más trabajo: configuración, documentación, código, etc.

El punto principal del artículo, en mi opinión, es que a veces un número es solo un número: no tiene ningún significado adicional, aparte de lo que se transmite en el código y no es probable que se use en otro lugar. Por lo tanto, resumir torpemente lo que está haciendo el código (ahora) en un nombre de variable solo para evitar valores codificados es, en el mejor de los casos, una repetición innecesaria.

Thanos Tintinidis
fuente
2
Si introdujo la constante LEDGER_AMOUNT_REQUIRING_AUTHLDG1A, ya no escribiría el comentario en el código. Los comentarios no son bien mantenidos por los programadores. Si la cantidad cambiara, la ifcondición y el comentario no estarían sincronizados. Por el contrario, la constante LEDGER_AMOUNT_REQUIRING_AUTHLDG1Anunca se desincroniza consigo misma y explica su propósito sin el comentario innecesario.
ZeroOne
2
@ZeroOne: Excepto que si la regla comercial cambia a "Libro mayor de 500K o más requiere AUTHLDG-1A y AUTHLDG-2B", es muy probable que la persona que agrega la attachDocument("AUTHLDG-2B");línea no actualice el nombre constante al mismo tiempo. En este caso, creo que el código es bastante claro sin un comentario ni una variable explicativa. (Aunque podría tener sentido tener una convención que indique la sección apropiada del documento de requisitos comerciales a través de comentarios de código. Según dicha convención, un comentario de código que haga eso sería apropiado aquí.)
ruakh
@ruakh, OK, entonces refactorizaría la constante a llamar LEDGER_AMOUNT_REQUIRING_ADDITIONAL_DOCUMENTS(lo que probablemente debería haber hecho en primer lugar). También tengo la costumbre de poner los ID de requisitos comerciales en los mensajes de confirmación de Git, no en el código fuente.
ZeroOne
1
@ZeroOne: Pero para AUTHLDG-3C, la cantidad del libro mayor es en realidad un máximo . Y para AUTHLDG-4D, la cantidad adecuada del libro mayor depende del estado. (¿Ya entiende el punto? Para este tipo de código, desea que su código refleje las reglas comerciales, no un intento de abstracción de las reglas comerciales, porque no hay razón para esperar que la evolución de las reglas comerciales se alinee con el abstracciones que has adoptado.)
ruakh
2
Personalmente, no me opongo a poner el número mágico en el código, me opongo a estructurar el código para que necesite estos comentarios. Si fuera yo, haría de cada documento una instancia de enumeración con su propio attachIfNecessary()método y simplemente recorrería todos ellos.
David Moles
8

Las otras respuestas son correctas y reflexivas. Pero aquí está mi respuesta corta y dulce.

  Rule/value          |      At Runtime, rule/value…
  appears in code:    |   …Is fixed          …Changes
----------------------|------------------------------------
                      |                 |
  Once                |   Hard-code     |   Externalize
                      |                 |   (soft-code)
                      |                 |
                      |------------------------------------
                      |                 |
  More than once      |   Soft-code     |   Externalize
                      |   (internal)    |   (soft-code)
                      |                 |
                      |------------------------------------

Si las reglas y los valores especiales aparecen en un lugar en el código y no cambian durante el tiempo de ejecución, codifique tal como se muestra en la pregunta.

Si las reglas o los valores especiales aparecen en más de un lugar en el código, y no cambian durante el tiempo de ejecución, entonces codifique suavemente. La codificación suave de una regla podría definir una clase / método específico o utilizar el patrón Builder . Para los valores, la codificación suave puede significar definir una sola constante o enumeración para el valor que se utilizará en su código.

Si las reglas o los valores especiales pueden cambiar durante el tiempo de ejecución, debe externalizarlos. Se realiza comúnmente actualizando valores en una base de datos. O actualice los valores en la memoria manualmente por un usuario que ingresa datos. También se realiza almacenando valores en un archivo de texto (XML, JSON, texto sin formato, lo que sea) que se escanea repetidamente para modificar el cambio de fecha y hora de la modificación del archivo.

Albahaca Bourque
fuente
1
Me gusta su respuesta, pero creo que también debe considerar si cambia en la implementación. Esto es principalmente relevante si se trata de un producto que se utilizará en muchas organizaciones que podrían, por ejemplo, tener reglas diferentes sobre si un supervisor necesita aprobar un reembolso sobre X, etc., etc.
Bloke Down The Pub
Estoy de acuerdo con esta respuesta y el comentario sobre la implementación. Las cosas en las que trabajo son implementadas por muchas organizaciones, y muchas de ellas tienen valores sutilmente diferentes necesarios. Tendemos a almacenar estas 'configuraciones' en una base de datos en lugar de un archivo de configuración, pero el principio es que no queremos hacer diferentes compilaciones de nuestro software para cada compañía que lo implementa (luego repetir esas compilaciones diferentes cada vez que se actualicen) .
RosieC
7

Esta es la trampa en la que caemos cuando usamos un problema de juguete y luego planteamos solo soluciones de sorpresa , cuando tratamos de ilustrar un problema real.

En el ejemplo dado, no importa si los valores dados están codificados como valores en línea, o definidos como consts.

Es el código circundante lo que haría del ejemplo un horror de mantenimiento y codificación. Si no es ningún código que rodea, a continuación, el fragmento está bien, al menos en un ambiente de refactorización constante. En un entorno donde la refactorización tiende a no ocurrir, los encargados de mantener ese código ya están muertos, por razones que pronto se harán evidentes.

Mira, si hay un código que lo rodea, entonces claramente suceden cosas malas.

Lo primero malo es que el valor 50000 se usa para otro valor en algún lugar, por ejemplo, el monto del libro mayor sobre el cual cambia la tasa impositiva en algunos estados ... luego, cuando ocurre el cambio, el mantenedor no tiene forma de saber cuándo encuentra esos dos instancias de 50000 en el código, ya sea que signifiquen el mismo 50k o 50k completamente no relacionados. ¿Y también debe buscar 49999 y 50001, en caso de que alguien los use también como constantes? Esta no es una llamada para incluir esas variables en un archivo de configuración de un servicio separado: pero codificarlas en línea también es claramente incorrecto. En cambio, deben ser constantes, definidas y definidas dentro de la clase o archivo en el que se usan. Si las dos instancias de 50k usan la misma constante, entonces probablemente representan la misma restricción legislativa; si no, probablemente no; y de cualquier manera, tendrán un nombre,

Los nombres de los archivos se pasan a una función, attachDocument (), que acepta los nombres de los archivos base como una cadena, sin ruta ni extensión. Los nombres de los archivos son, esencialmente, claves foráneas para algún sistema de archivos, o base de datos, o de donde attachDocument () obtiene los archivos. Pero las cadenas no le dicen nada sobre esto: ¿cuántos archivos hay? ¿Qué tipos de archivo son? ¿Cómo sabe, al abrir un nuevo mercado, si necesita actualizar esta función? ¿A qué tipo de cosas se pueden unir? El mantenedor se queda completamente en la oscuridad, y todo lo que tiene es una cadena, que puede aparecer varias veces en el código y significa diferentes cosas cada vez que aparece. En un lugar, "SR008-04X" es un código trampa. En otro, es un comando ordenar cuatro cohetes de refuerzo SR008. Aquí' sa nombre de archivo? ¿Están relacionados? Alguien acaba de cambiar esa función para mencionar otro archivo, "CLIENTE". Entonces, pobre mantenedor, le han dicho que el archivo "CLIENTE" debe ser renombrado como "CLIENTE". Pero la cadena "CLIENTE" aparece 937 veces en el código ... ¿dónde empiezas a buscar?

El problema del juguete es que los valores son todos inusuales y se puede garantizar razonablemente que sean únicos en el código. No "1" o "10" sino "50,000". No "cliente" o "informe" sino "SR008-04X".

El problema es que la única otra forma de abordar el problema de las constantes opacas impenetrables es colocarlas en el archivo de configuración de algún servicio no relacionado.

Juntos, pueden usar estas dos falacias para demostrar que cualquier argumento es verdadero.

Dewi Morgan
fuente
2
No es un problema de juguete, no es un hombre de paja. Esto es algo que verá todo el tiempo en este tipo de aplicaciones comerciales. No hay "apertura a un nuevo mercado", no hay reutilización del mismo número (después de todo, eso le daría otro significado de todos modos) y, en cualquier caso, el artículo no dice nada en contra de DRY: si hay dos dependencias del valor, se moverá a un método o una constante. Muestre ejemplos de cómo deben nombrarse esas constantes (de la configuración, realmente no importa) y dónde deben almacenarse de una manera que sea a prueba de futuro y más clara que el código.
Luaan
44
El ejemplo no se descompone porque es un problema de juguete. El código circundante siempre será horrible porque las reglas de negocio que el software debe ejecutar son horrorosas . Intenta esquivar este desafío fundamental con motores de reglas y DSL y otras cosas que a menudo es la postergación del programador , porque resolver los problemas de CS es más agradable que resolver las complejidades de los formularios de impuestos. Los intentos de lograr la 'elegancia' son a menudo tonterías porque la tarea final del software es modelar un desastre complicado.
whatsisname
Las reglas comerciales pueden ser horrorosas, pero eso no es en sí mismo una excusa para escribir este tipo de código de procedimiento mediocre. (Tiendo a estar de acuerdo con Papadimoulis en que es más fácil modelar y mantener las reglas en el código que en la configuración, solo creo que debería ser un mejor código). El problema SECO que veo no son los números mágicos, son los repetidos if (...) { attachDocument(...); }.
David Moles
2

Hay varios problemas en esto.

Un problema es si se debe construir un motor de reglas para que todas las reglas sean fácilmente configurables fuera del programa. La respuesta en casos similares a esto es a menudo no. Las reglas cambiarán de formas extrañas que son difíciles de predecir, lo que significa que el motor de reglas debe ampliarse siempre que haya un cambio.

Otro problema es cómo manejar estas reglas y sus cambios en el control de su versión. La mejor solución aquí es dividir las reglas en una clase para cada regla.

Eso permite que cada regla tenga su propia validez, algunas reglas cambian cada año, algunas cambian dependiendo de cuándo se ha otorgado un permiso o se emite una factura. La regla misma que contiene la verificación de la versión que tiene que aplicar.

Además, como la constante es privada, no se puede utilizar de forma incorrecta en ningún otro lugar del código.

Luego tenga una lista de todas las reglas y aplique la lista.

Otro problema es cómo manejar las constantes. 500000 puede parecer discreto, pero se debe tener mucho cuidado para asegurarse de que se convierta correctamente. Si se aplica una aritmética de coma flotante, podría convertirse a 500,000.00001, por lo que podría fallar una comparación con 500,000.00000. O peor aún, 500000 siempre funciona según lo previsto, pero de alguna manera 565000 falla cuando se convierte. Asegúrese de que la conversión sea explícita y que usted no la haya compilado adivinando. A menudo, esto se hace convirtiéndolo en BigInteger o BigDecimal antes de que se use.

Doblado
fuente
2

Si bien no se menciona directamente en la pregunta, me gustaría señalar que lo importante es no enterrar la lógica de negocios en el código.

El código, como el ejemplo anterior, que codifica los requisitos comerciales especificados externamente realmente debería vivir en una parte distinta del árbol de origen, quizás nombrado businesslogico algo similar, y se debe tener cuidado para garantizar que solo codifique los requisitos comerciales de manera simple, legible y de manera concisa como sea posible, con un mínimo de repetitivo y con comentarios claros e informativos.

Debería no ser mezclado con el código de "infraestructura" que implementa la funcionalidad necesaria para llevar a cabo la lógica de negocio, tales como, por ejemplo, la puesta en práctica del attachDocument()método en el ejemplo, o por ejemplo, la interfaz de usuario, registro o código de base de datos en general. Si bien una forma de imponer esta separación es "codificar" toda la lógica de negocios en un archivo de configuración, esto está lejos de ser el único (o el mejor) método.

Dicho código de lógica de negocios también debe escribirse con la suficiente claridad como para que, si se lo mostraras a un experto en dominios de negocios sin habilidades de codificación, podrían tener sentido. Por lo menos, si los requisitos comerciales cambian, el código que los codifica debe ser lo suficientemente claro como para que incluso un nuevo programador sin familiaridad previa con la base de código pueda localizar, revisar y actualizar fácilmente la lógica comercial, suponiendo que no se requiere ninguna funcionalidad cualitativamente nueva.

Idealmente, dicho código también se escribiría en un lenguaje específico de dominio para forzar la separación entre la lógica empresarial y la infraestructura subyacente, pero eso puede ser innecesariamente complicado para una aplicación interna básica. Dicho esto, si por ejemplo está vendiendo el software a múltiples clientes que necesitan cada uno su propio conjunto personalizado de reglas comerciales, un lenguaje de scripting específico de dominio simple (tal vez, por ejemplo, basado en un sandbox Lua ) podría ser la solución.

Ilmari Karonen
fuente
¡Esto es exactamente lo que estaba pensando! Cuando la lógica está profundamente enterrada en el código, ¿cómo puede un experto en el dominio / tema o un usuario comercial ver los valores y la lógica que se utilizan para garantizar que sean correctos y diagnosticar el comportamiento del sistema? Una cosa que hace un archivo de configuración es hacer visibles las configuraciones . Tiene que haber algunos medios para promover la visibilidad de las reglas comerciales, incluso si eso hace que la codificación sea "más difícil". Puedo aceptar una clase delgada o un conjunto de clases que hacen el trabajo, sin mezclar otras preocupaciones, siempre y cuando el usuario comercial tenga los medios para acceder a ellas y comprenderlas.
ErikE