¿Existe un nombre para el patrón (anti) de pasar parámetros que solo se utilizarán en varios niveles en la cadena de llamadas?

209

Estaba tratando de encontrar alternativas al uso de la variable global en algún código heredado. Pero esta pregunta no se trata de las alternativas técnicas, me preocupa principalmente la terminología .

La solución obvia es pasar un parámetro a la función en lugar de usar un global. En esta base de código heredada, eso significaría que tengo que cambiar todas las funciones en la larga cadena de llamadas entre el punto donde finalmente se usará el valor y la función que recibe el parámetro primero.

higherlevel(newParam)->level1(newParam)->level2(newParam)->level3(newParam)

donde newParamanteriormente era una variable global en mi ejemplo, pero podría haber sido un valor previamente codificado en su lugar. El punto es que ahora el valor de newParam se obtiene higherlevel()y tiene que "viajar" hasta el final level3().

Me preguntaba si había un nombre (s) para este tipo de situación / patrón donde necesita agregar un parámetro a muchas funciones que simplemente "pasan" el valor sin modificar.

Con suerte, usar la terminología adecuada me permitirá encontrar más recursos sobre soluciones para rediseñar y describir esta situación a mis colegas.

ecerulm
fuente
94
Esta es una mejora sobre el uso de variables globales. Deja en claro exactamente de qué estado depende cada función (y es un paso en el camino hacia las funciones puras). He escuchado que se llama "threading" un parámetro, pero no sé qué tan común es esta terminología.
cabeza de jardín
8
Este es un espectro demasiado amplio para tener una respuesta específica. En este nivel, llamaría a esto simplemente "codificación".
Machado
38
Creo que el "problema" es solo la simplicidad. Esto es básicamente inyección de dependencia. Supongo que podría haber mecanismos que inyectarían automáticamente la dependencia a través de la cadena, si algún miembro más anidado lo tiene, sin inflar las listas de parámetros de las funciones. Tal vez mirar estrategias de inyección de dependencia con diferentes niveles de sofisticación podría conducir a la terminología que está buscando, si hay una.
nulo
77
Aunque aprecio la discusión sobre si es un buen patrón / antipatrón / concepto / solución, lo que realmente quería saber es si hay un nombre para él.
Ecerulm
3
También he escuchado que se llama subprocesamiento con mayor frecuencia, pero también plomería , como al bajar una línea de plomada en toda la pila de llamadas.
wchargin

Respuestas:

202

Los datos en sí mismos se denominan "datos de vagabundo" . Es un "olor a código", que indica que un código se está comunicando con otro código a distancia, a través de intermediarios.

  • Aumenta la rigidez del código, especialmente en la cadena de llamadas. Estás mucho más limitado en cómo refactorizar cualquier método en la cadena de llamadas.
  • Distribuye conocimiento sobre datos / métodos / arquitectura a lugares que no les importa lo más mínimo. Si necesita declarar los datos que se están transfiriendo y la declaración requiere una nueva importación, ha contaminado el espacio de nombres.

Refactorizar para eliminar variables globales es difícil, y los datos de vagabundeo son un método para hacerlo, y a menudo la forma más barata. Tiene sus costos.

BobDalgleish
fuente
73
Al buscar "datos de vagabundo" pude encontrar el libro "Código completo" en mi suscripción de Safari. Hay una sección en el libro llamada "Razones para usar datos globales" y una de las razones es "El uso de datos globales puede eliminar datos de vagabundos". :). Siento que los "datos de vagabundo" me permitirán encontrar más literatura sobre cómo tratar con los globales. ¡Gracias!
Ecerulm
99
@JimmyJames, esas funciones hacen cosas, por supuesto. Simplemente no con ese nuevo parámetro específico que anteriormente era solo global.
Ecerulm
174
En 20 años de programación, literalmente nunca antes había escuchado este término ni habría sido inmediatamente obvio lo que significaba. No me quejo de la respuesta, solo sugiero que el término no es tan ampliamente utilizado / conocido. Tal vez solo soy yo.
Derek Elkins el
66
Algunos datos globales están bien. En lugar de llamarlo "datos globales", puede llamarlo "entorno", porque eso es lo que es. El entorno puede incluir, por ejemplo, una ruta de cadena para appdata (en Windows) o, en mi proyecto actual, el conjunto completo de pinceles, lápices, fuentes, etc. GDI +, utilizados por todos los componentes.
Robinson el
77
@ Robinson No del todo. Por ejemplo, ¿realmente quiere que su código de escritura de imagen toque% AppData%, o prefiere que tome un argumento sobre dónde escribir? Esa es la diferencia entre el estado global y un argumento. El "entorno" puede ser fácilmente una dependencia inyectada, solo presente para aquellos que son responsables de interactuar con el entorno. Los cepillos GDI +, etc., son más razonables, pero en realidad es más un caso de administración de recursos en un entorno que no puede hacerlo por usted, más o menos una deficiencia de las API subyacentes y / o su idioma / bibliotecas / tiempo de ejecución.
Luaan
102

No creo que esto, en sí mismo, sea un antipatrón. Creo que el problema es que estás pensando en las funciones como una cadena cuando realmente deberías pensar en cada una como una caja negra independiente ( NOTA : los métodos recursivos son una notable excepción a este consejo).

Por ejemplo, supongamos que necesito calcular el número de días entre dos fechas de calendario para crear una función:

int daysBetween(Day a, Day b)

Para hacer esto, creo una nueva función:

int daysSinceEpoch(Day day)

Entonces mi primera función se convierte simplemente:

int daysBetween(Day a, Day b)
{
    return daysSinceEpoch(b) - daysSinceEpoch(a);
}

No hay nada antipatrón sobre esto. Los parámetros del método daysBetween se pasan a otro método y nunca se hace referencia de otro modo en el método, pero aún son necesarios para que ese método haga lo que debe hacer.

Lo que recomendaría es mirar cada función y comenzar con un par de preguntas:

  • ¿Esta función tiene un objetivo claro y enfocado o es un método de "hacer algunas cosas"? Por lo general, el nombre de la función ayuda aquí y si hay cosas en él que no se describen por el nombre, eso es una bandera roja.
  • ¿Hay demasiados parámetros? A veces, un método puede legítimamente necesitar mucha información, pero tener tantos parámetros hace que sea difícil de usar o comprender.

Si está buscando una mezcla de código sin un solo propósito incluido en un método, debe comenzar desentrañando eso. Esto puede ser tedioso. Comience con las cosas más fáciles de sacar y pase a un método separado y repita hasta que tenga algo coherente.

Si solo tiene demasiados parámetros, considere la refactorización de Método a Objeto .

JimmyJames
fuente
2
Bueno, no quise ser controvertido con el (anti). Pero todavía me pregunto si hay un nombre para la "situación" de tener que actualizar muchas firmas de funciones. Supongo que es más un "código de olor" que un antipatrón. Me dice que hay algo que arreglar en este código heredado, si tengo que actualizar la firma de 6 funciones para acomodar la eliminación de un código global. Pero sí creo que pasar parámetros suele estar bien, y agradezco la información sobre cómo abordar el problema subyacente.
ecerulm
44
@ecerulm No conozco uno, pero diré que mi experiencia me dice que convertir los globales en parámetros es absolutamente la forma correcta de comenzar a eliminarlos. Esto elimina el estado compartido para que pueda refactorizar aún más. Supongo que hay más problemas con este código, pero no hay suficiente en su descripción para saber cuáles son.
JimmyJames
2
Por lo general, también sigo ese enfoque, y probablemente también lo haga en este caso. Solo quería mejorar mi vocabulario / terminología en torno a esto para poder investigar más sobre esto y hacer mejores y más centradas preguntas en el futuro.
ecerulm
3
@ecerulm No creo que haya un nombre para esto. Es como un síntoma que es común a muchas enfermedades, así como a enfermedades que no son enfermedades, por ejemplo, 'boca seca'. Si desarrolla la descripción de la estructura del código, podría señalar algo específico.
JimmyJames
@ecerulm Te dice que hay algo que arreglar, ahora es mucho más obvio que algo debe arreglarse de lo que era cuando era una variable global.
immibis
61

BobDalgleish ya ha notado que este patrón (anti) se llama " datos de vagabundo ".

En mi experiencia, la causa más común de datos de vagabundeo excesivos es tener un montón de variables de estado vinculadas que realmente deberían encapsularse en un objeto o una estructura de datos. A veces, incluso puede ser necesario anidar un montón de objetos para organizar adecuadamente los datos.

Para un ejemplo simple, considere un juego que tiene un personaje de jugador personalizable, con propiedades como playerName, playerEyeColoretc. Por supuesto, el jugador también tiene una posición física en el mapa del juego y varias otras propiedades como, por ejemplo, el nivel de salud actual y máximo, y así sucesivamente.

En una primera iteración de un juego de este tipo, podría ser una opción perfectamente razonable convertir todas estas propiedades en variables globales: después de todo, solo hay un jugador, y casi todo en el juego de alguna manera involucra al jugador. Entonces su estado global puede contener variables como:

playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100

Pero en algún momento, es posible que necesites cambiar este diseño, tal vez porque deseas agregar un modo multijugador al juego. Como primer intento, puede intentar hacer que todas esas variables sean locales y pasarlas a las funciones que las necesitan. Sin embargo, puede encontrar que una acción particular en su juego podría involucrar una cadena de llamada de función como, por ejemplo:

mainGameLoop()
 -> processInputEvent()
     -> doPlayerAction()
         -> movePlayer()
             -> checkCollision()
                 -> interactWithNPC()
                     -> interactWithShopkeeper()

... y la interactWithShopkeeper()función hace que el comerciante dirija al jugador por su nombre, por lo que ahora de repente debe pasar playerNamecomo datos de vagabundo a través de todas esas funciones. Y, por supuesto, si el comerciante cree que los jugadores de ojos azules son ingenuos y cobrarán precios más altos por ellos, entonces necesitará pasar playerEyeColorpor toda la cadena de funciones, y así sucesivamente.

La solución adecuada , en este caso, es, por supuesto, definir un objeto jugador que encapsule el nombre, color de ojos, posición, salud y cualquier otra propiedad del personaje jugador. De esa manera, solo necesita pasar ese único objeto a todas las funciones que de alguna manera involucran al jugador.

Además, varias de las funciones anteriores podrían convertirse naturalmente en métodos de ese objeto jugador, lo que automáticamente les daría acceso a las propiedades del jugador. En cierto modo, esto es solo azúcar sintáctico, ya que llamar a un método en un objeto efectivamente pasa la instancia del objeto como un parámetro oculto al método de todos modos, pero hace que el código se vea más claro y más natural si se usa correctamente.

Por supuesto, un juego típico tendría mucho más estado "global" que solo el jugador; por ejemplo, es casi seguro que tengas algún tipo de mapa en el que se desarrolla el juego, y una lista de personajes que no son jugadores que se mueven en el mapa, y tal vez elementos colocados en él, y así sucesivamente. También podría pasar todos esos objetos vagabundos, pero eso volvería a saturar los argumentos de su método.

En cambio, la solución es hacer que los objetos almacenen referencias a cualquier otro objeto con el que tengan relaciones permanentes o temporales. Entonces, por ejemplo, el objeto jugador (y probablemente también cualquier objeto NPC) probablemente debería almacenar una referencia al objeto "mundo del juego", que tendría una referencia al nivel / mapa actual, de modo que un método como player.moveTo(x, y)no necesita se le dará explícitamente el mapa como parámetro.

De manera similar, si nuestro personaje jugador tuviera, por ejemplo, un perro mascota que los siguiera, naturalmente agruparíamos todas las variables de estado que describen al perro en un solo objeto, y le daríamos al objeto jugador una referencia al perro (para que el jugador pueda , por ejemplo, llame al perro por su nombre) y viceversa (para que el perro sepa dónde está el jugador). Y, por supuesto, probablemente querríamos que el jugador y el perro se conviertan en subclases de un objeto "actor" más genérico, de modo que podamos reutilizar el mismo código para, por ejemplo, mover ambos por el mapa.

PD. Aunque he usado un juego como ejemplo, hay otros tipos de programas en los que también surgen estos problemas. Sin embargo, en mi experiencia, el problema subyacente tiende a ser siempre el mismo: tiene un montón de variables separadas (ya sean locales o globales) que realmente quieren agruparse en uno o más objetos interconectados. Si los "datos de vagabundo" que se entrometen en sus funciones consisten en configuraciones de opciones "globales" o consultas de bases de datos en caché o vectores de estado en una simulación numérica, la solución es invariablemente identificar el contexto natural al que pertenecen los datos y convertirlo en un objeto (o lo que sea el equivalente más cercano en el idioma elegido).

Ilmari Karonen
fuente
1
Esta respuesta proporciona algunas soluciones a una clase de problemas que pueden existir. Puede haber situaciones en las que se utilizaron globales que indicarían una solución diferente. Tengo un problema con la idea de que hacer que los métodos formen parte de la clase de jugador es equivalente a pasar objetos a métodos. Esto ignora el polimorfismo que no se replica fácilmente de esta manera. Por ejemplo, si quiero crear diferentes tipos de jugadores que tengan diferentes reglas sobre movimientos y diferentes tipos de propiedades, simplemente pasar estos objetos a la implementación de un método requerirá mucha lógica condicional.
JimmyJames
66
@JimmyJames: Su punto sobre el polimorfismo es bueno, y pensé en hacerlo yo mismo, pero lo dejé afuera para evitar que la respuesta se alargue aún más. El punto que estaba tratando de hacer (quizás mal) fue que, aunque solo en términos de flujo de datos, hay poca diferencia foo.method(bar, baz)y method(foo, bar, baz)hay otras razones (incluido el polimorfismo, la encapsulación, la localidad, etc.) para preferir el primero.
Ilmari Karonen
@IlmariKaronen: también el beneficio muy obvio de que protege los prototipos de la función de futuros cambios / adiciones / eliminaciones / refactorizaciones en los objetos (por ejemplo, playerAge). Esto solo es invaluable.
smci
34

No conozco un nombre específico para esto, pero creo que vale la pena mencionar que el problema que describe es solo el problema de encontrar el mejor compromiso para el alcance de dicho parámetro:

  • Como variable global, el alcance es demasiado grande cuando el programa alcanza cierto tamaño

  • como un parámetro puramente local, el alcance puede ser demasiado pequeño, cuando conduce a muchas listas de parámetros repetitivos en cadenas de llamadas

  • entonces, como compensación, a menudo puede hacer que dicho parámetro sea una variable miembro en una o más clases, y eso es lo que yo llamaría un diseño de clase adecuado .

Doc Brown
fuente
10
+1 para un diseño de clase adecuado. Esto suena como un problema clásico esperando una solución OO.
l0b0
21

Creo que el patrón que estás describiendo es exactamente una inyección de dependencia . Varios comentaristas han argumentado que este es un patrón , no un antipatrón , y yo tendería a estar de acuerdo.

También estoy de acuerdo con la respuesta de @ JimmyJames, donde afirma que es una buena práctica de programación tratar cada función como un cuadro negro que toma todas sus entradas como parámetros explícitos. Es decir, si está escribiendo una función que hace un sándwich de mantequilla de maní y mermelada, podría escribirlo como

Sandwich make_sandwich() {
    PeanutButter pb = get_peanut_butter();
    Jelly j = get_jelly();
    return pb + j;
}
extern PhysicalRefrigerator g_refrigerator;
PeanutButter get_peanut_butter() {
    return g_refrigerator.get("peanut butter");
}
Jelly get_jelly() {
    return g_refrigerator.get("jelly");
}

pero sería una mejor práctica aplicar la inyección de dependencia y escribirla así:

Sandwich make_sandwich(Refrigerator& r) {
    PeanutButter pb = get_peanut_butter(r);
    Jelly j = get_jelly(r);
    return pb + j;
}
PeanutButter get_peanut_butter(Refrigerator& r) {
    return r.get("peanut butter");
}
Jelly get_jelly(Refrigerator& r) {
    return r.get("jelly");
}

Ahora tiene una función que documenta claramente todas sus dependencias en su firma de función, lo cual es excelente para facilitar la lectura. Después de todo, es cierto que para poder make_sandwichnecesitar acceder a un Refrigerator; así que la antigua firma de la función era básicamente falsa al no tomar el refrigerador como parte de sus entradas.

Como beneficio adicional, si hace bien su jerarquía de clases, evite cortar, y así sucesivamente, ¡incluso puede probar la make_sandwichfunción de la unidad pasando un MockRefrigerator! (Es posible que deba realizar una prueba unitaria de esta manera porque su entorno de prueba unitaria podría no tener acceso a ningún PhysicalRefrigerators).

Entiendo que no todos los usos de la inyección de dependencia requieren conectar un parámetro con un nombre similar a muchos niveles en la pila de llamadas, por lo que no estoy respondiendo exactamente la pregunta que hizo ... pero si está buscando más información sobre este tema, "inyección de dependencia" es definitivamente una palabra clave relevante para usted.

Quuxplusone
fuente
10
Esto es muy claramente un anti patrón. No hay absolutamente ninguna llamada para pasar un refrigerador. Ahora, pasar un IngredientSource genérico podría funcionar, pero ¿qué pasa si obtienes el pan del pan, el atún de la despensa, el queso de la nevera ... inyectando una dependencia de la fuente de ingredientes en la operación no relacionada de formar esos ingredientes en un sándwich, has violado la separación de preocupaciones y has hecho un olor a código.
Dewi Morgan el
8
@DewiMorgan: Obviamente, podría refactorizar aún más para generalizar el Refrigeratoren un IngredientSource, o incluso generalizar la noción de "sandwich" en template<typename... Fillings> StackedElementConstruction<Fillings...> make_sandwich(ElementSource&); eso se llama "programación genérica" ​​y es razonablemente poderoso, pero seguramente es mucho más arcano de lo que el OP realmente quiere entrar en este momento. Siéntase libre de abrir una nueva pregunta sobre el nivel adecuado de abstracción para los programas sandwich. ;)
Quuxplusone el
11
No se equivoque, el usuario no privilegiado no debería tener acceso make_sandwich().
dotancohen
2
@Dewi - Enlace
Gavin Lock
19
El error más grave en su código es que mantiene la mantequilla de maní en el refrigerador.
Malvolio
15

Esta es más o menos la definición de libro de texto de acoplamiento , un módulo que tiene una dependencia que afecta profundamente a otro, y que crea un efecto dominó cuando se cambia. Los otros comentarios y respuestas son correctos de que esta es una mejora con respecto a la global, porque el acoplamiento ahora es más explícito y más fácil de ver para el programador, en lugar de subversivo. Eso no significa que no deba repararse. Debería poder refactorizar para quitar o reducir el acoplamiento, aunque si ha estado allí por un tiempo, puede ser doloroso.

Karl Bielefeldt
fuente
3
Si es level3()necesario newParam, eso es seguro, pero de alguna manera diferentes partes del código tienen que comunicarse entre sí. No llamaría necesariamente un mal acoplamiento de un parámetro de función si esa función hace uso del parámetro. Creo que el aspecto problemático de la cadena es el acoplamiento adicional introducido level1()y level2()que no tiene ningún uso newParamexcepto para transmitirlo. Buena respuesta, +1 por acoplamiento.
nulo
66
@null Si realmente no lo hubieran usado, podrían inventar un valor en lugar de recibirlo de su interlocutor.
Random832
3

Si bien esta respuesta no responde directamente a su pregunta, creo que sería negligente dejarla pasar sin mencionar cómo mejorarla (ya que, como usted dice, puede ser un antipatrón). Espero que usted y otros lectores puedan obtener valor de este comentario adicional sobre cómo evitar los "datos de vagabundo" (como Bob Dalgleish lo llamó tan útilmente para nosotros).

Estoy de acuerdo con las respuestas que sugieren hacer algo más OO para evitar este problema. Sin embargo, otra forma de ayudar a reducir profundamente este paso de argumentos sin simplemente saltar a "¡ simplemente pasar una clase donde solías pasar muchos argumentos! " Es refactorizar para que algunos pasos de tu proceso ocurran en el nivel superior en lugar del nivel inferior uno. Por ejemplo, aquí hay un código anterior :

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   FilterAndReportStuff(stuffs, desiredName);
}

public void FilterAndReportStuff(IEnumerable<Stuff> stuffs, string desiredName) {
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   ReportStuff(stuffs.Filter(filter));
}

public void ReportStuff(IEnumerable<Stuff> stuffs) {
   stuffs.Report();
}

Tenga en cuenta que esto se vuelve aún peor a medida que se hacen más cosas ReportStuff. Es posible que deba pasar la instancia del Reportero que desea usar. Y todo tipo de dependencias que deben transmitirse, función a función anidada.

Mi sugerencia es llevar todo eso a un nivel superior, donde el conocimiento de los pasos requiere vidas en un solo método en lugar de extenderse a través de una cadena de llamadas a métodos. Por supuesto, sería más complicado en código real, pero esto te da una idea:

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   var filteredStuffs = stuffs.Filter(filter)
   filteredStuffs.Report();
}

Tenga en cuenta que la gran diferencia aquí es que no tiene que pasar las dependencias a través de una cadena larga. Incluso si se aplana no solo a un nivel, sino a unos pocos niveles de profundidad, si esos niveles también logran algo de "aplanamiento" para que el proceso se vea como una serie de pasos en ese nivel, habrá mejorado.

Si bien esto todavía es de procedimiento y nada se ha convertido en un objeto todavía, es un buen paso para decidir qué tipo de encapsulación puede lograr al convertir algo en una clase. Las llamadas de método profundamente encadenadas en el escenario anterior ocultan los detalles de lo que realmente está sucediendo y pueden hacer que el código sea muy difícil de entender. Si bien puede exagerar esto y terminar haciendo que el código de nivel superior sepa sobre cosas que no debería, o hacer un método que haga demasiadas cosas violando así el principio de responsabilidad única, en general he descubierto que aplanar un poco las cosas ayuda en claridad y en hacer un cambio incremental hacia un mejor código.

Tenga en cuenta que mientras hace todo esto, debe considerar la capacidad de prueba. El método encadenado en realidad llama hacer las pruebas unitarias más difícil , ya que no tiene un buen punto de entrada y punto de salida en el montaje para el sector que desee probar. Tenga en cuenta que con este aplanamiento, ya que sus métodos ya no requieren tantas dependencias, son más fáciles de probar, ¡y no requieren tantos simulacros!

Recientemente intenté agregar pruebas unitarias a una clase (que no escribí) que tomó algo así como 17 dependencias, ¡todas las cuales tuvieron que ser burladas! Todavía no lo tengo todo resuelto, pero dividí la clase en tres clases, cada una de las cuales trataba con uno de los sustantivos por separado que tenía, y obtuve la lista de dependencia a 12 para el peor y alrededor de 8 para el el mejor.

La capacidad de prueba lo obligará a escribir un mejor código. Debería escribir pruebas unitarias porque descubrirá que le hace pensar en su código de manera diferente y escribirá un código mejor desde el principio, independientemente de los pocos errores que haya tenido antes de escribir las pruebas unitarias.

ErikE
fuente
2

No estás literalmente violando la Ley de Deméter, pero tu problema es similar a eso de alguna manera. Dado que el objetivo de su pregunta es encontrar recursos, le sugiero que lea sobre la Ley de Demeter y vea cuánto de ese consejo se aplica a su situación.

comida de gato
fuente
1
Un poco débil en los detalles, lo que probablemente explica los votos negativos. Sin embargo, en espíritu, esta respuesta es exactamente acertada: OP debería leer sobre la Ley de Demeter: este es el término relevante.
Konrad Rudolph el
44
FWIW, no creo que la Ley de Deméter (también conocido como "privilegio mínimo") sea relevante en absoluto. El caso del OP es donde su función no sería capaz de hacer su trabajo si no tuviera los datos de vagabundo (porque el siguiente tipo en la pila de llamadas lo necesita, porque el siguiente tipo lo necesita, y así sucesivamente). El privilegio mínimo / Ley de Demeter es relevante solo si el parámetro no se utiliza realmente , y en ese caso la solución es obvia: ¡elimine el parámetro no utilizado!
Quuxplusone
2
La situación de esta pregunta no tiene exactamente nada que ver con la Ley de Demeter ... Hay una similitud superficial con respecto a la cadena de llamadas a métodos, pero por lo demás es muy diferente.
Eric King el
@Quuxplusone Posible, aunque en este caso la descripción es bastante confusa porque las llamadas encadenadas realmente no tienen sentido en ese escenario: deberían anidarse en su lugar.
Konrad Rudolph el
1
El problema es muy similar a las violaciones de LoD, ya que la refactorización habitual sugerida para tratar las violaciones de LoD es introducir datos de vagabundo. En mi humilde opinión, este es un buen punto de partida para reducir el acoplamiento, pero no es suficiente.
Jørgen Fogh
1

Hay casos en los que lo mejor (en términos de eficiencia, facilidad de mantenimiento y facilidad de implementación) para tener ciertas variables como globales en lugar de la sobrecarga de siempre pasar todo (digamos que tiene aproximadamente 15 variables que deben persistir). Por lo tanto, tiene sentido encontrar un lenguaje de programación que admita mejor el alcance (como las variables estáticas privadas de C ++) para aliviar el desorden potencial (del espacio de nombres y la manipulación de las cosas). Por supuesto, esto es solo de conocimiento común.

Pero, el enfoque establecido por el OP es muy útil si uno está haciendo Programación Funcional.

kozner
fuente
0

No hay ningún antipatrón aquí en absoluto, porque la persona que llama no conoce todos estos niveles a continuación y no le importa.

Alguien está llamando al nivel más alto (params) y espera que el nivel más alto haga su trabajo. Lo que hace HighLevel con los parámetros no es asunto de quienes llaman. higherLevel maneja el problema de la mejor manera posible, en este caso pasando parámetros al nivel 1 (parámetros). Eso está absolutamente bien.

Ves una cadena de llamadas, pero no hay una cadena de llamadas. Hay una función en la parte superior que hace su trabajo de la mejor manera posible. Y hay otras funciones. Cada función puede ser reemplazada en cualquier momento.

gnasher729
fuente