¿Es incorrecto usar un parámetro booleano para determinar valores?

39

Según ¿Está mal usar un parámetro booleano para determinar el comportamiento? , Sé la importancia de evitar el uso de parámetros booleanos para determinar un comportamiento, por ejemplo:

Versión original

public void setState(boolean flag){
    if(flag){
        a();
    }else{
        b();
    }
    c();
}

nueva versión:

public void setStateTrue(){
    a();
    c();
}

public void setStateFalse(){
    b();
    c();
}

Pero, ¿qué tal el caso de que el parámetro booleano se use para determinar valores en lugar de comportamientos? p.ej:

public void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

Estoy tratando de eliminar el indicador isHintOn y crear 2 funciones separadas:

public void setHintOn(){
    this.layer1.visible=true;
    this.layer2.visible=false;
    this.layer3.visible=true;
}

public void setHintOff(){
    this.layer1.visible=false;
    this.layer2.visible=true;
    this.layer3.visible=false;
}

pero la versión modificada parece menos mantenible porque:

  1. tiene más códigos que la versión original

  2. no puede mostrar claramente que la visibilidad de layer2 es opuesta a la opción de sugerencia

  3. cuando se agrega una nueva capa (por ejemplo: layer4), necesito agregar

    this.layer4.visible=false;
    

    y

    this.layer4.visible=true;  
    

    en setHintOn () y setHintOff () por separado

Entonces, mi pregunta es, si el parámetro booleano se usa solo para determinar valores, pero no comportamientos (por ejemplo: no if-else en ese parámetro), ¿todavía se recomienda eliminar ese parámetro booleano?

ocomfd
fuente
26
Nunca está mal si el código resultante es más fácil de leer y mantener ;-) Recomendaría usar el método único en lugar de los dos métodos separados.
Helb
32
Presenta un argumento convincente de que una implementación única de un método que establece estos valores booleanos resultará en un mantenimiento más fácil de la clase y la comprensión de su implementación. Muy bien; Esas son consideraciones legítimas. Pero la interfaz pública de la clase no necesita deformarse para acomodarlos. Si los métodos separados harán que la interfaz pública más fácil de entender y trabajar con, definir su setHint(boolean isHintOn)como privada método, y añadir pública setHintOny setHintOffmétodos que llaman, respectivamente, setHint(true)y setHint(false).
Mark Amery
9
Estaría muy descontento con esos nombres de métodos: realmente no ofrecen ningún beneficio setHint(true|false). Potahto de papa. Al menos usa algo como setHinty unsetHint.
Konrad Rudolph el
44
@kevincline Si la condición es un nombre, escribe isal principio. isValidetc. Entonces, ¿por qué cambiar eso por dos palabras? Además, "más natural" está en el ojo del espectador. Si desea pronunciarlo como una oración en inglés, entonces para mí sería más natural tener "si la pista está activada" con "el" escondido.
Sr. Lister

Respuestas:

95

El diseño de la API debe centrarse en lo que es más útil para un cliente de la API, desde el lado de la llamada .

Por ejemplo, si esta nueva API requiere que la persona que llama escriba regularmente código como este

if(flag)
    foo.setStateTrue();
else
    foo.setStateFalse();

entonces debería ser obvio que evitar el parámetro es peor que tener una API que permita a la persona que llama escribir

 foo.setState(flag);

La versión anterior solo produce un problema que luego debe resolverse en el lado de la llamada (y probablemente más de una vez). Eso no aumenta la legibilidad ni la mantenibilidad.

El lado de la implementación , sin embargo, no debe dictar cómo las miradas de la API pública gusta. Si una función como setHintcon un parámetro necesita menos código en la implementación, pero una API en términos setHintOn/ setHintOffparece más fácil de usar para un cliente, se puede implementar de esta manera:

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

Entonces, aunque la API pública no tiene un parámetro booleano, no hay una lógica duplicada aquí, por lo que solo hay un lugar para cambiar cuando llega un nuevo requisito (como en el ejemplo de la pregunta).

Esto también funciona al revés: si el setStatemétodo anterior necesita cambiar entre dos partes de código diferentes, esas partes de código pueden refactorizarse a dos métodos privados diferentes. Entonces, en mi humilde opinión, no tiene sentido buscar un criterio para decidir entre "un parámetro / un método" y "cero parámetros / dos métodos" mirando las partes internas. Mire, sin embargo, la forma en que le gustaría ver la API en el papel de un consumidor de la misma.

En caso de duda, intente utilizar el "desarrollo impulsado por pruebas" (TDD), que lo obligará a pensar en la API pública y cómo usarla.

Doc Brown
fuente
DocBrown, ¿diría que la línea divisoria adecuada es si la configuración de cada estado tiene efectos secundarios complejos y posiblemente no reversibles? Por ejemplo, si simplemente está alternando una bandera que hace lo que dice en la lata, y no hay una máquina de estado subyacente, parametrizará los diferentes estados. Mientras que, por ejemplo, no parametrizaría un método como SetLoanFacility(bool enabled), porque después de haber otorgado un préstamo, puede que no sea fácil quitarlo de nuevo, y las dos opciones pueden involucrar una lógica completamente diferente, y querría pasar a separar Crear / Eliminar métodos.
Steve
15
@Steve: todavía está intentando diseñar la API a partir de los requisitos que ve en el lado de la implementación. Para ser sincero: eso es totalmente irrelevante. Use cualquier variante de la API pública que sea más fácil de usar desde el lado de la llamada. Internamente, siempre puede permitir que dos métodos públicos llamen a uno privado con un parámetro. O viceversa, puede dejar que un método con un parámetro cambie entre dos métodos privados con lógica diferente.
Doc Brown
@ Steve: mira mi edición.
Doc Brown
Tomo todos sus puntos: de hecho, estoy pensando en ello desde el lado de la persona que llama (de ahí mi referencia a "lo que dice en la lata"), y tratando de formular la regla apropiada donde una persona que llama generalmente esperaría usar cada enfoque. Me parece que la regla es si la persona que llama espera que las llamadas repetidas sean idempotentes y si las transiciones de estado no tienen restricciones y no tienen efectos secundarios complejos. El encendido y apagado de un interruptor de luz de la habitación se parametrizaría, y el encendido y apagado de una estación de energía regional sería un método múltiple.
Steve
1
@Steve Entonces, si el usuario necesita afirmar una configuración específica, entonces Toggle()no es la función correcta que debe proporcionar. Ese es todo el punto; Si la persona que llama solo se preocupa por "cambiarlo" y no por "lo que termina como", entonces Toggle()es la opción que evita la verificación y la toma de decisiones adicionales. No llamaría a eso un caso COMÚN, ni recomendaría ponerlo a disposición sin una buena razón, pero si el usuario necesita un cambio, entonces les daría un cambio.
Kamil Drakari
40

Martin Fowler cita a Kent Beck al recomendar setOn() setOff()métodos separados , pero también dice que esto no debe considerarse inviolable:

Si la extracción de datos [sic] de una fuente booleana, como un control de interfaz de usuario o fuente de datos, yo prefiero setSwitch(aValue)que

if (aValue)
  setOn();
else
  setOff();

Este es un ejemplo de que se debe escribir una API para que sea más fácil para la persona que llama, por lo que si sabemos de dónde proviene, deberíamos diseñar la API teniendo en cuenta esa información. Esto también argumenta que a veces podemos proporcionar ambos estilos si recibimos llamadas de ambas maneras.

Otra recomendación es usar un valor enumerado o un tipo de marca para dar truey falsemejores nombres específicos del contexto. En su ejemplo, showHinty hideHintpodría ser mejor.

Graham Lee
fuente
16
En mi experiencia, setSwitch (value) casi siempre da como resultado menos código general que setOn / setOff precisamente por el código if / else citado en su respuesta. Tiendo a maldecir a los desarrolladores que me dan una API de setOn / setOff en lugar de setSwitch (value).
17 de 26
1
Mirándolo desde una lente similar: si necesita codificar cuál será el valor, eso es fácil con cualquiera. Sin embargo, si necesita establecerlo en, digamos, entrada del usuario, si solo puede pasar el valor directamente, eso ahorra un paso.
Financia la demanda de Mónica el
@ 17of26 como un contraejemplo concreto (para mostrar que "depende" en lugar de que uno sea preferible al otro), en el AppKit de Apple hay un -[NSView setNeedsDisplay:]método por el que se pasa YESsi una vista se debe volver a dibujar y NOsi no. Casi nunca necesita decirle que no lo haga, por lo que UIKit simplemente no tiene -[UIView setNeedsDisplay]ningún parámetro. No tiene el -setDoesNotNeedDisplaymétodo correspondiente .
Graham Lee
2
@GrahamLee, creo que el argumento de Fowler es bastante sutil y realmente se basa en el juicio. No usaría una bool isPremiumbandera en su ejemplo, pero usaría una enum ( BookingType bookingType) para parametrizar el mismo método, a menos que la lógica para cada reserva fuera bastante diferente. La "lógica enredada" a la que se refiere Fowler es a menudo deseable si uno quiere poder ver cuál es la diferencia entre los dos modos. Y si son radicalmente diferentes, expondría el método parametrizado externamente e implementaría los métodos separados internamente.
Steve
3

Creo que estás mezclando dos cosas en tu publicación, la API y la implementación. En ambos casos, no creo que haya una regla fuerte que puedas usar todo el tiempo, pero debes considerar estas dos cosas de forma independiente (tanto como sea posible).

Comencemos con la API, ambos:

public void setHint(boolean isHintOn)

y:

public void setHintOn()
public void setHintOff()

son alternativas válidas según lo que se supone que debe ofrecer su objeto y cómo sus clientes utilizarán la API. Como señaló Doc, si sus usuarios ya tienen una variable booleana (desde un control de UI, una acción del usuario, una API externa, etc.), la primera opción tiene más sentido, de lo contrario, solo está forzando una declaración if adicional en el código del cliente . Sin embargo, si, por ejemplo, está cambiando la pista a verdadero al comenzar un proceso y a falso al final, la primera opción le ofrece algo como esto:

setHint(true)
// Do your process
…
setHint(false)

mientras que la segunda opción te da esto:

setHintOn()
// Do your process
…
setHintOff()

cuál IMO es mucho más legible, así que iré con la segunda opción en este caso. Obviamente, nada le impide ofrecer ambas opciones (o más, podría usar una enumeración como dijo Graham si eso tiene más sentido, por ejemplo).

El punto es que debe elegir su API en función de lo que se supone que debe hacer el objeto y de cómo los clientes lo van a usar, no de cómo lo va a implementar.

Luego debe elegir cómo implementar su API pública. Digamos que elegimos los métodos setHintOny setHintOffcomo nuestra API pública y comparten esta lógica común como en su ejemplo. Puede abstraer fácilmente esta lógica a través de un método privado (código copiado de Doc):

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

Por el contrario, supongamos que elegimos setHint(boolean isHintOn)nuestra API, pero invirtamos su ejemplo, debido a la razón por la cual la sugerencia Activada es completamente diferente a la desactivada. En este caso podríamos implementarlo de la siguiente manera:

public void setHint(boolean isHintOn){
    if(isHintOn){
        // Set it On
    } else {
        // Set it Off
    }    
}

O incluso:

public void setHint(boolean isHintOn){    
    if(isHintOn){
        setHintOn()
    } else {
        setHintOff()
   }    
}

private void setHintOn(){
   // Set it On
}

private void setHintOff(){
   // Set it Off 
}

El punto es que, en ambos casos, primero elegimos nuestra API pública y luego adaptamos nuestra implementación para que se ajuste a la API elegida (y las restricciones que tenemos), y no al revés.

Por cierto, creo que lo mismo se aplica a la publicación que vinculó sobre el uso de un parámetro booleano para determinar el comportamiento, es decir, debe decidir en función de su caso de uso particular en lugar de alguna regla estricta (aunque en ese caso en particular generalmente lo correcto es hacer es romperlo en múltiples funciones).

jesm00
fuente
3
Como nota al margen, si es algo así como el pseudobloque (una declaración al principio, una al final), probablemente debería usar beginy / endo sinónimos, solo para dejar explícitamente claro que eso es lo que hacen, e implicar que un el comienzo tiene que tener un final y viceversa.
Financia la demanda de Mónica el
Estoy de acuerdo con Nic, y agregaría: si desea asegurarse de que los comienzos y los fines estén siempre acoplados, también debe proporcionar expresiones idiomáticas específicas de idioma para eso: RAII / guardias de alcance en C ++, usingbloques en C #, withadministradores de contexto de declaración en Python, pasando el cuerpo de como un objeto lambda u invocable (por ejemplo, sintaxis de bloque Ruby), etc.
Daniel Pryden
Estoy de acuerdo con ambos puntos, solo estaba tratando de dar un ejemplo simple para ilustrar el otro caso (aunque es un mal ejemplo como ambos señalaron :)).
jesm00
2

Primero lo primero: el código no es menos fácil de mantener automáticamente, solo porque es un poco más largo. La claridad es lo que importa.

Ahora, si realmente solo está tratando con datos, entonces lo que tiene es un setter para una propiedad booleana. En ese caso, es posible que desee almacenar ese valor directamente y obtener las visibilidades de la capa, es decir

bool isBackgroundVisible() {
    return isHintVisible;
}    

bool isContentVisible() {
    return !isHintVisible;
}

(Me tomé la libertad de dar nombres reales a las capas; si no tiene esto en su código original, comenzaría con eso)

Esto todavía te deja con la pregunta de si tener un setHintVisibility(bool)método. Personalmente, recomendaría reemplazarlo con un método showHint()y hideHint(), ambos serán muy simples y no tendrá que cambiarlos cuando agregue capas. Sin embargo, no es un claro claro / incorrecto.

Ahora, si llamar a la función realmente debería cambiar la visibilidad de esas capas, en realidad tiene un comportamiento. En ese caso, definitivamente recomendaría métodos separados.

te doblo
fuente
Entonces tl; dr es: ¿Recomiendas dividir en dos funciones porque no tendrás que cambiarlas cuando agregues capas? Si se agrega una capa 4, es posible que también necesitemos decir "this.layer4.visibility = isHintOn", por lo que no estoy seguro de estar de acuerdo. En todo caso, es un demérito, ya que ahora cuando se agrega una capa tenemos que editar dos funciones, no solo una.
Erdrik Ironrose
No, lo recomiendo (débilmente) por claridad ( showHintvs setHintVisibility). Mencioné la nueva capa solo porque OP estaba preocupada por eso. Además, sólo tenemos que añadir un método nuevo: isLayer4Visible. showHinty hideHintsimplemente establece el isHintVisibleatributo en verdadero / falso y eso no cambia.
doubleYou
1
@doubleYou, usted dice que un código más largo no es menos fácil de mantener automáticamente. Diría que la longitud del código es una de las principales variables en mantenibilidad y claridad, superada solo por la complejidad estructural. Cualquier código que se haga más largo y estructuralmente más complejo debería merecerlo, de lo contrario, los problemas simples reciben un tratamiento más complejo en el código del que merecen, y la base de código adquiere matorrales de líneas innecesarias (el problema de "demasiados niveles de indirección").
Steve
@Steve Estoy completamente de acuerdo en que puedes hacer un diseño excesivo de las cosas, es decir, hacer que el código sea más largo sin dejarlo más claro: otoh, siempre puedes hacer el código más corto a costa de la claridad, por lo que no hay una relación 1: 1 aquí.
doubleYou
Piense @Steve “código de campo” - teniendo dicho código y volver a escribir en más líneas no suelen hacerlo más claro. "Code golf" es un extremo, pero todavía hay muchos programadores que piensan que agrupar todo en una expresión inteligente es "elegante" y tal vez incluso más rápido porque los compiladores no están optimizando lo suficientemente bien.
BlackJack
1

Los parámetros booleanos están bien en el segundo ejemplo. Como ya ha descubierto, los parámetros booleanos no son en sí mismos problemáticos. Está cambiando el comportamiento basado en una bandera, lo cual es problemático.

Sin embargo, el primer ejemplo es problemático porque el nombre indica un método de establecimiento, pero las implementaciones parecen ser algo diferente. Entonces tiene el antipatrón de cambio de comportamiento y un método engañosamente nombrado. Pero si el método en realidad es un configurador regular (sin cambio de comportamiento), entonces no hay problema con setState(boolean). Tener dos métodos, setStateTrue()y setStateFalse()simplemente complica innecesariamente las cosas sin ningún beneficio.

JacquesB
fuente
1

Otra forma de resolver este problema es introducir un objeto para representar cada pista, y hacer que el objeto sea responsable de determinar los valores booleanos asociados con esa pista. De esta manera, puede agregar nuevas permutaciones en lugar de simplemente tener dos estados booleanos.

Por ejemplo, en Java, podría hacer:

public enum HintState {
    SHOW_HINT(true, false, true),
    HIDE_HINT(false, true, false);

    private HintState(boolean layer1Visible, boolean layer2Visible, boolean layer3Visible) {
         // constructor body and accessors omitted for clarity
    }
}

Y luego su código de llamada se vería así:

setHint(HintState.SHOW_HINT);

Y su código de implementación se vería así:

public void setHint(HintState hint) {
    this.layer1Visible = hint.isLayer1Visible();
    this.layer2Visible = hint.isLayer2Visible();
    this.layer3Visible = hint.isLayer3Visible();
}

Esto mantiene conciso el código de implementación y el código de la persona que llama, a cambio de definir un nuevo tipo de datos que mapee claramente las intenciones con nombres fuertemente tipados a los conjuntos de estados correspondientes. Creo que eso es mejor por todos lados.

Daniel Pryden
fuente
0

Entonces, mi pregunta es, si el parámetro booleano se usa solo para determinar valores, pero no comportamientos (por ejemplo: no if-else en ese parámetro), ¿todavía se recomienda eliminar ese parámetro booleano?

Cuando tengo dudas sobre tales cosas. Me gusta imaginar cómo se vería el rastro de la pila.

Durante muchos años trabajé en un proyecto PHP que usaba la misma función como setter y getter . Si pasó nulo , devolvería el valor; de lo contrario, configúrelo. Fue horrible trabajar con él .

Aquí hay un ejemplo de seguimiento de pila:

function visible() : line 440
function parent() : line 398
function mode() : line 384
function run() : line 5

No tenía idea del estado interno y esto hacía que la depuración fuera más difícil. Hay muchos otros efectos secundarios negativos, pero trate de ver si hay un valor en el nombre detallado de una función y claridad cuando las funciones realizan una sola acción.

Ahora imagine trabajar con el seguimiento de pila para una función que tiene un comportamiento A o B basado en un valor booleano.

function bar() : line 92
function setVisible() : line 120
function foo() : line 492
function setVisible() : line 120
function run() : line 5

Eso es confuso si me preguntas. Las mismas setVisiblelíneas producen dos trazados diferentes.

Así que volvamos a tu pregunta. Intente imaginar cómo se verá el seguimiento de la pila, cómo se comunica a una persona lo que está sucediendo y pregúntese si está ayudando a una persona futura a depurar el código.

Aquí hay algunos consejos:

  • un nombre de función claro que implica intención sin necesidad de conocer los valores del argumento.
  • una función realiza una sola acción
  • el nombre implica mutación o comportamiento inmutable
  • resuelva desafíos de depuración en relación con la capacidad de depuración del lenguaje y las herramientas.

A veces, el código se ve excesivo bajo un microscopio, pero cuando regresa a la imagen más grande, un enfoque minimalista lo hará desaparecer. Si necesita que se destaque para poder mantenerlo. Agregar muchas funciones pequeñas puede parecer excesivamente detallado, pero mejora la capacidad de mantenimiento cuando se usa ampliamente en un contexto más amplio.

Reactgular
fuente
1
Lo que me confunde sobre el setVisibleseguimiento de la pila es que las llamadas setVisible(true)parecen dar como resultado una llamada a setVisible(false)(o al revés, dependiendo de cómo se produjo ese seguimiento).
David K
0

En casi todos los casos en los que pasa un booleanparámetro a un método como indicador para cambiar el comportamiento de algo, debe considerar una forma más explícita y segura de escribir para hacerlo.

Si no hace nada más que usar un Enumque represente el estado, ha mejorado la comprensión de su código.

Este ejemplo usa la Nodeclase de JavaFX:

public enum Visiblity
{
    SHOW, HIDE

    public boolean toggleVisibility(@Nonnull final Node node) {
        node.setVisible(!node.isVisible());
    }
}

siempre es mejor que, como se encuentra en muchos JavaFXobjetos:

public void setVisiblity(final boolean flag);

pero creo .setVisible()y .setHidden()es la mejor solución para la situación en la que la bandera es una, booleanya que es la más explícita y menos detallada.

en el caso de algo con múltiples selecciones, esto es aún más importante hacerlo de esta manera. EnumSetexiste solo por esta razón.

Ed Mann tiene una muy buena publicación de blog sobre este tema. Estaba a punto de parafrasear exactamente lo que dice, así que para no duplicar el esfuerzo, solo publicaré un enlace a su publicación de blog como un anexo a esta respuesta.


fuente
0

Al decidir entre un enfoque para alguna interfaz que pasa un parámetro (booleano) frente a métodos sobrecargados sin dicho parámetro, mire a los clientes consumidores.

Si todos los usos pasarían valores constantes (por ejemplo, verdadero, falso), entonces eso argumenta a favor de las sobrecargas.

Si todos los usos pasaran un valor variable, entonces eso argumenta a favor del método con enfoque de parámetros.

Si ninguno de esos extremos se aplica, eso significa que hay una combinación de usos de los clientes, por lo que debe elegir si admite ambos formularios o hacer que una categoría de clientes se adapte al otro (lo que para ellos es un estilo más antinatural).

Erik Eidt
fuente
¿Qué pasa si usted está diseñando un sistema integrado y que son en última instancia, tanto el productor de código y el "cliente consumidor" del código? ¿Cómo debe una persona parada en el lugar de un cliente consumidor formular su preferencia por un enfoque sobre otro?
Steve
@Steve, como cliente consumidor, sabes si estás pasando una constante o una variable. Si pasa constantes, prefiera sobrecargas sin el parámetro.
Erik Eidt
Pero estoy interesado en articular por qué ese debería ser el caso. ¿Por qué no emplear enumeraciones para un número limitado de constantes, ya que esa es una sintaxis ligera en la mayoría de los idiomas diseñados precisamente para este tipo de propósito?
Steve
@ Steve, si sabemos en tiempo de diseño / tiempo de compilación que los clientes usarían un valor constante (verdadero / falso) en todos los casos, entonces eso sugiere que realmente hay dos métodos específicos diferentes en lugar de un método generalizado (que toma un parámetro). Yo argumentaría en contra de la introducción de la generalidad de un método parametrizado cuando no se utiliza, es un argumento YAGNI.
Erik Eidt
0

Hay dos consideraciones para diseñar:

  • API: qué interfaz presentas al usuario,
  • Implementación: claridad, mantenimiento, etc.

No deben ser combinados.

Está perfectamente bien:

  • tener múltiples métodos en el delegado API a una sola implementación,
  • tener un único método en el envío de API a múltiples implementaciones dependiendo de la condición.

Como tal, cualquier argumento que intente equilibrar los costos / beneficios de un diseño de API con los costos / beneficios de un diseño de implementación es dudoso y debe examinarse cuidadosamente.


En el lado de la API

Como programador, generalmente favoreceré las API programables. El código es mucho más claro cuando puedo reenviar un valor que cuando necesito una declaración if/ switchen el valor para decidir qué función llamar.

Esto último puede ser necesario si cada función espera argumentos diferentes.

En su caso, por lo tanto, un único método setState(type value)parece mejor.

Sin embargo , no hay nada peor que sin nombre true, false, 2, etc ... esos valores mágicos no tienen significado por sí mismos. Evite la obsesión primitiva y adopte la mecanografía fuerte.

Por lo tanto, desde un POV API, quiero: setState(State state).


En el lado de la implementación

Recomiendo hacer lo que sea más fácil.

Si el método es simple, es mejor mantenerlo unido. Si el flujo de control es complicado, es mejor separarlo en varios métodos, cada uno de los cuales se ocupa de un sub-caso o un paso de la tubería.


Finalmente, considere la agrupación .

En su ejemplo (con espacio en blanco agregado para facilitar la lectura):

this.layer1.visible = isHintOn;
this.layer2.visible = ! isHintOn;
this.layer3.visible = isHintOn;

¿Por qué está layer2resistiendo la tendencia? ¿Es una característica o es un error?

¿Podría ser posible tener 2 listas [layer1, layer3]y [layer2], con un nombre explícito que indique la razón por la que están agrupadas, y luego iterar sobre esas listas?

Por ejemplo:

for (auto layer : this.mainLayers) { // layer2
    layer.visible = ! isHintOn;
}
for (auto layer : this.hintLayers) { // layer1 and layer3
    layer.visible = isHintOn;
}

El código habla por sí mismo, está claro por qué hay dos grupos y su comportamiento es diferente.

Matthieu M.
fuente
0

Por separado de la pregunta setOn() + setOff()vs set(flag), consideraría cuidadosamente si un tipo booleano es mejor aquí. ¿Estás seguro de que nunca habrá una tercera opción?

Puede valer la pena considerar una enumeración en lugar de un booleano. Además de permitir la extensibilidad, esto también hace que sea más difícil obtener el valor booleano de manera incorrecta, por ejemplo:

setHint(false)

vs

setHint(Visibility::HIDE)

Con la enumeración, será mucho más fácil extender cuando alguien decida que quiere una opción 'si es necesario':

enum class Visibility {
  SHOW,
  HIDE,
  IF_NEEDED // New
}

vs

setHint(false)
setHint(true)
setHintAutomaticMode(true) // New
Phil
fuente
0

Según ..., sé la importancia de evitar el uso de parámetros booleanos para determinar un comportamiento

Sugeriría reevaluar este conocimiento.

En primer lugar, no veo la conclusión que está proponiendo en la pregunta SE que ha vinculado. Principalmente hablan de reenviar un parámetro a través de varios pasos de llamadas a métodos, donde se evalúa muy lejos en la cadena.

En su ejemplo, está evaluando el parámetro directamente en su método. A ese respecto, no difiere en absoluto de ningún otro tipo de parámetro.

En general, no hay absolutamente nada de malo en usar parámetros booleanos; y obviamente cualquier parámetro determinará un comportamiento, o ¿por qué lo tendrías en primer lugar?

AnoE
fuente
0

Definiendo la pregunta

Su pregunta del título es "¿Está mal [...]?" - Pero, ¿qué quieres decir con "mal"?

Según un compilador C # o Java, no está mal. Estoy seguro de que eres consciente de eso y no es lo que estás preguntando. Aparte de eso, me temo que solo tenemos nprogramadores con n+1opiniones diferentes. Esta respuesta presenta lo que el libro Clean Code tiene que decir sobre esto.

Responder

Clean Code presenta un argumento sólido contra los argumentos de la función en general:

Los argumentos son difíciles. Toman mucho poder conceptual. [...] nuestros lectores habrían tenido que interpretarlo cada vez que lo vieran.

Un "lector" aquí puede ser el consumidor de API. También puede ser el próximo programador, que aún no sabe qué hace este código, que podría ser usted en un mes. Pasarán por 2 funciones por separado o por 1 función dos veces , una teniendo en cuenta truey otra falseen mente.
En resumen, use la menor cantidad de argumentos posible .

El caso específico de un argumento de bandera se aborda más adelante directamente:

Los argumentos de la bandera son feos. Pasar un booleano a una función es una práctica realmente terrible. Inmediatamente complica la firma del método, proclamando en voz alta que esta función hace más de una cosa. ¡Hace una cosa si la bandera es verdadera y otra si la bandera es falsa!

Para responder sus preguntas directamente:
De acuerdo con Clean Code , se recomienda eliminar ese parámetro.


Información adicional:

Su ejemplo es bastante simple, pero incluso allí puede ver la sencillez que se propaga a su código: las funciones sin parámetros solo realizan tareas simples, mientras que la otra función tiene que hacer aritmética booleana para alcanzar el mismo objetivo. Es una aritmética booleana trivial en este ejemplo simplificado, pero podría ser bastante complejo en una situación real.


He visto muchos argumentos aquí de que deberías hacerlo depender del usuario de la API, porque tener que hacer esto en muchos lugares sería estúpido:

if (isAfterSunset) light.TurnOn();
else light.TurnOff();

Yo no estoy de acuerdo de que algo no óptima está pasando aquí. Tal vez sea demasiado obvio para ver, pero su primera oración menciona "la importancia de evitar [sic] usar parámetros booleanos para determinar un comportamiento" y esa es la base de toda la pregunta. No veo una razón para hacer que lo que es malo sea más fácil para el usuario de la API.


No sé si haces pruebas, en ese caso, también considera esto:

Los argumentos son aún más difíciles desde el punto de vista de la prueba. Imagine la dificultad de escribir todos los casos de prueba para asegurarse de que todas las diversas combinaciones de argumentos funcionen correctamente. Si no hay argumentos, esto es trivial.

R. Schmitz
fuente
Realmente has enterrado el lede aquí: "... tu primera oración menciona" la importancia de evitar [sic] usar parámetros booleanos para determinar un comportamiento "y esa es la base de toda la pregunta. No veo una razón para hacer que lo que es malo sea más fácil para el usuario de la API ". Este es un punto interesante, pero más bien socava su propio argumento en su último párrafo.
Comodín el
Enterrado el lede? El núcleo de esta respuesta es "usar la menor cantidad de argumentos posible", que se explica en la primera mitad de esta respuesta. Todo después de eso es solo información adicional: refutar un argumento contradictorio (por otro usuario, no OP) y algo que no se aplica a todos.
R. Schmitz
El último párrafo solo trata de aclarar que la pregunta del título no está lo suficientemente bien definida como para ser respondida. OP pregunta si está "mal", pero no dice de acuerdo a quién o qué. De acuerdo con el compilador? Parece un código válido, así que no está mal. Según el libro Clean Code? Utiliza un argumento de bandera, así que sí, está "mal". Sin embargo, escribo "recomendado" en su lugar, porque pragmático> dogmático. ¿Crees que necesito aclarar eso?
R. Schmitz
así que espera, ¿tu defensa de tu respuesta es que la pregunta del título no es muy clara para ser respondida? : D Ok ... De hecho, pensé que el punto que cité fue una nueva toma interesante.
Comodín el
1
Vívidamente claro ahora; ¡buen trabajo!
Comodín el