Un colega mío afirma que los booleanos como argumentos de método no son aceptables . Serán reemplazados por enumeraciones. Al principio no vi ningún beneficio, pero él me dio un ejemplo.
¿Qué es más fácil de entender?
file.writeData( data, true );
O
enum WriteMode {
Append,
Overwrite
};
file.writeData( data, Append );
¡Ahora lo tengo! ;-)
Este es definitivamente un ejemplo en el que una enumeración como segundo parámetro hace que el código sea mucho más legible.
Entonces, ¿cuál es tu opinión sobre este tema?
coding-style
boolean
enumeration
Thomas Koschel
fuente
fuente
Respuestas:
Los booleanos representan opciones de "sí / no". Si desea representar un "sí / no", utilice un booleano, debe explicarse por sí mismo.
Pero si se trata de elegir entre dos opciones, ninguna de las cuales es claramente sí o no, entonces una enumeración a veces puede ser más legible.
fuente
setLightOn(bool)
.Las enumeraciones también permiten modificaciones futuras, donde ahora desea una tercera opción (o más).
fuente
Use el que mejor modele su problema. En el ejemplo que da, la enumeración es una mejor opción. Sin embargo, habría otros momentos en que un booleano es mejor. Lo que tiene más sentido para ti:
o
En este caso, podría elegir la opción booleana ya que creo que es bastante clara e inequívoca, y estoy bastante seguro de que mi bloqueo no tendrá más de dos estados. Aún así, la segunda opción es válida, pero innecesariamente complicada, en mi humilde opinión.
fuente
Para mí, ni usar booleano ni enumeración es un buen enfoque. Robert C. Martin capta esto muy claramente en su Código limpio Consejo # 12: Eliminar argumentos booleanos :
Si un método hace más de una cosa, debería escribir dos métodos diferentes, por ejemplo en su caso:
file.append(data)
yfile.overwrite(data)
.Usar una enumeración no aclara las cosas. No cambia nada, sigue siendo un argumento distintivo.
fuente
setVisible(boolean visible) { mVisible = visible; }
. ¿Cuál sería una alternativa que sugerirías?Creo que casi respondiste esto tú mismo, creo que el objetivo final es hacer que el código sea más legible, y en este caso la enumeración hizo eso, en mi opinión, siempre es mejor mirar el objetivo final en lugar de las reglas generales, tal vez piense más en ello como guía, es decir, las enumeraciones a menudo son más legibles en el código que los bools genéricos, ints, etc., pero siempre habrá excepciones a la regla.
fuente
¿Recuerda la pregunta que Adlai Stevenson le hizo al embajador Zorin en la ONU durante la crisis de los misiles cubanos ?
Si la bandera que tiene en su método es de tal naturaleza que puede precisarla como una decisión binaria , y esa decisión nunca se convertirá en una decisión de tres vías o de dos vías, vaya a boolean. Indicaciones: su bandera se llama isXXX .
No lo haga booleano en caso de que sea un cambio de modo . Siempre hay un modo más de lo que pensabas al escribir el método en primer lugar.
El dilema del modo uno más ha perseguido, por ejemplo, a Unix, donde los posibles modos de permiso que puede tener un archivo o directorio pueden dar como resultado un extraño doble sentido de modos dependiendo del tipo de archivo, propiedad, etc.
fuente
Hay dos razones por las que me he encontrado con esto como algo malo:
Porque algunas personas escribirán métodos como:
Obviamente, esto es malo porque es demasiado fácil mezclar parámetros, y no tienes idea al mirar lo que estás especificando. Sin embargo, solo un bool no es tan malo.
Debido a que controlar el flujo del programa mediante una rama simple sí / no puede significar que tiene dos funciones completamente diferentes que se agrupan en una de una manera extraña. Por ejemplo:
Realmente, esto debería ser dos métodos
porque el código en estos podría ser completamente diferente; Es posible que tengan que hacer todo tipo de manejo y validación de errores diferentes, o tal vez incluso tengan que formatear los datos salientes de manera diferente. No puede decirlo simplemente usando
Write()
o inclusoWrite(Enum.Optical)
(aunque, por supuesto, podría tener cualquiera de esos métodos, simplemente llame a los métodos internos WriteOptical / Mag si lo desea).Supongo que solo depende. No haría gran cosa al respecto, excepto por el # 1.
fuente
Las enumeraciones son mejores, pero no llamaría a los parámetros booleanos como "inaceptables". A veces es más fácil lanzar un pequeño booleano y seguir adelante (piense en métodos privados, etc.)
fuente
Los booleanos pueden estar bien en lenguajes que tienen parámetros con nombre, como Python y Objective-C, ya que el nombre puede explicar lo que hace el parámetro:
o:
fuente
No estaría de acuerdo en que es una buena regla . Obviamente, Enum crea un mejor código explícito o detallado en algunos casos, pero, por regla general, parece exagerado.
Primero déjenme tomar su ejemplo: la responsabilidad (y capacidad) de los programadores para escribir un buen código no se ve realmente comprometida por tener un parámetro booleano. En su ejemplo, el programador podría haber escrito igual que el código detallado escribiendo:
o prefiero más general
Segundo: El ejemplo de Enum que diste es solo "mejor" porque estás pasando un CONST. Lo más probable en la mayoría de las aplicaciones, al menos algunos, si no la mayoría de los parámetros de tiempo que se pasan a las funciones son VARIABLES. en cuyo caso mi segundo ejemplo (dar variables con buenos nombres) es mucho mejor y Enum le habría dado pocos beneficios.
fuente
Las enumeraciones tienen un beneficio definitivo, pero no debes simplemente reemplazar todas tus booleanas por enumeraciones. Hay muchos lugares donde verdadero / falso es en realidad la mejor manera de representar lo que está sucediendo.
Sin embargo, usarlos como argumentos de método es un poco sospechoso, simplemente porque no puedes ver sin profundizar en las cosas lo que se supone que deben hacer, ya que te permiten ver lo que realmente significa verdadero / falso
Las propiedades (especialmente con los inicializadores de objetos C # 3) o los argumentos de palabras clave (a la ruby o python) son una forma mucho mejor de ir a donde de otro modo usaría un argumento booleano.
Ejemplo de C #:
Ejemplo de rubí
Ejemplo de Python
Lo único que se me ocurre cuando un argumento de método booleano es lo correcto es en Java, donde no tiene propiedades ni argumentos de palabras clave. Esta es una de las razones por las que odio Java :-(
fuente
Si bien es cierto que en muchos casos las enumeraciones son más legibles y más extensibles que los booleanos, una regla absoluta de que "los booleanos no son aceptables" es tonta. Es inflexible y contraproducente, no deja espacio para el juicio humano. Son un tipo incorporado fundamental en la mayoría de los idiomas porque son útiles; considere aplicarlo a otros tipos incorporados: decir, por ejemplo, "nunca usar un int como parámetro" sería una locura.
Esta regla es solo una cuestión de estilo, no de posibles errores o rendimiento en tiempo de ejecución. Una mejor regla sería "preferir enumeraciones a booleanos por razones de legibilidad".
Mira el marco .Net. Los booleanos se usan como parámetros en bastantes métodos. La API .Net no es perfecta, pero no creo que el uso de booleanos como parámetros sea un gran problema. La información sobre herramientas siempre le da el nombre del parámetro, y también puede crear este tipo de orientación: complete sus comentarios XML sobre los parámetros del método, aparecerán en la información sobre herramientas.
También debería agregar que hay un caso en el que claramente debería refactorizar booleanos a una enumeración: cuando tiene dos o más booleanos en su clase o en los parámetros de su método, y no todos los estados son válidos (por ejemplo, no es válido tenerlos ambos se ponen verdaderos).
Por ejemplo, si su clase tiene propiedades como
Y es un error tener ambas cosas verdaderas al mismo tiempo, lo que realmente tienes son tres estados válidos, mejor expresados como:
fuente
Algunas reglas a las que su compañero podría adherirse mejor son:
fuente
Un booleano solo sería aceptable si no tiene la intención de ampliar la funcionalidad del marco. Se prefiere la enumeración porque puede extender la enumeración y no romper implementaciones anteriores de la llamada a la función.
La otra ventaja de Enum es que es más fácil de leer.
fuente
Si el método hace una pregunta como:
dónde
Los argumentos del método booleano parecen tener un sentido absolutamente perfecto.
fuente
Depende del método. Si el método hace algo que obviamente es algo verdadero / falso, entonces está bien, por ejemplo, a continuación [aunque no estoy diciendo que este sea el mejor diseño para este método, es solo un ejemplo de dónde es obvio el uso].
Sin embargo, en la mayoría de los casos, como el ejemplo que menciona, es mejor usar una enumeración. Hay muchos ejemplos en .NET Framework en sí mismos en los que no se sigue esta convención, pero eso se debe a que introdujeron esta directriz de diseño bastante tarde en el ciclo.
fuente
Hace las cosas un poco más explícitas, pero comienza a extender masivamente la complejidad de sus interfaces, en una elección booleana pura, como agregar / sobrescribir, parece excesivo. Si necesita agregar una opción adicional (que no se me ocurre en este caso), siempre puede realizar una refactorización (dependiendo del idioma)
fuente
Las enumeraciones ciertamente pueden hacer que el código sea más legible. Todavía hay algunas cosas a tener en cuenta (al menos en .net)
Debido a que el almacenamiento subyacente de una enumeración es un int, el valor predeterminado será cero, por lo que debe asegurarse de que 0 sea un valor predeterminado razonable. (Por ejemplo, las estructuras tienen todos los campos establecidos en cero cuando se crean, por lo que no hay forma de especificar un valor predeterminado que no sea 0. Si no tiene un valor 0, ni siquiera puede probar la enumeración sin convertir a int, que sería mal estilo)
Si sus enumeraciones son privadas para su código (nunca se exponen públicamente), puede dejar de leer aquí.
Si sus enumeraciones se publican de alguna manera en código externo y / o se guardan fuera del programa, considere numerarlas explícitamente. El compilador los numera automáticamente desde 0, pero si reorganiza sus enumeraciones sin darles valores, puede terminar con defectos.
Puedo escribir legalmente
Para combatir esto, cualquier código que consuma una enumeración de la que no pueda estar seguro (por ejemplo, API pública) debe verificar si la enumeración es válida. Lo haces a través de
La única advertencia
Enum.IsDefined
es que usa la reflexión y es más lento. También sufre un problema de versiones. Si necesita verificar el valor de enumeración a menudo, sería mejor lo siguiente:El problema del control de versiones es que el código antiguo solo puede saber cómo manejar las 2 enumeraciones que tiene. Si agrega un tercer valor, Enum.IsDefined será verdadero, pero el código antiguo no necesariamente puede manejarlo. Whoops
Puede hacer incluso más diversión con las
[Flags]
enumeraciones, y el código de validación para eso es ligeramente diferente.También voy a señalar que para la portabilidad, debe utilizar la llamada
ToString()
en la enumeración, y el usoEnum.Parse()
al leerlos de nuevo. TantoToString()
yEnum.Parse()
puedo manejar[Flags]
enumeración es así, así que no hay razón para no usarlos. Eso sí, es otra trampa, porque ahora ni siquiera puedes cambiar el nombre de la enumeración sin posiblemente romper el código.Entonces, a veces necesitas sopesar todo lo anterior cuando te preguntas ¿Puedo salirte con la suya?
fuente
En mi humilde opinión, parece que una enumeración sería la opción obvia para cualquier situación en la que sean posibles más de dos opciones. Pero definitivamente HAY situaciones en las que un booleano es todo lo que necesita. En ese caso, diría que usar una enumeración donde funcionaría un bool sería un ejemplo de uso de 7 palabras cuando 4 funcionarán.
fuente
Los booleanos tienen sentido cuando tienes una palanca obvia que solo puede ser una de dos cosas (es decir, el estado de una bombilla, encendida o apagada). Aparte de eso, es bueno escribirlo de tal manera que sea obvio lo que está pasando, por ejemplo, las escrituras en disco (sin búfer, con búfer de línea o síncrono) deben pasarse como tales. Incluso si no desea permitir escrituras síncronas ahora (y, por lo tanto, está limitado a dos opciones), vale la pena considerar que sean más detalladas para saber lo que hacen a primera vista.
Dicho esto, también puede usar Falso y Verdadero (booleano 0 y 1) y luego, si necesita más valores más adelante, expanda la función para admitir valores definidos por el usuario (digamos, 2 y 3), y sus viejos valores 0/1 se portará bien, por lo que su código no debería romperse.
fuente
A veces es más simple modelar diferentes comportamientos con sobrecargas. Continuar con su ejemplo sería:
Este enfoque se degrada si tiene múltiples parámetros, cada uno de los cuales permite un conjunto fijo de opciones. Por ejemplo, un método que abre un archivo puede tener varias permutaciones de modo de archivo (abrir / crear), acceso a archivos (lectura / escritura), modo de compartir (ninguno / lectura / escritura). El número total de configuraciones es igual a los productos cartesianos de las opciones individuales. Naturalmente, en tales casos, las sobrecargas múltiples no son apropiadas.
Las enumeraciones pueden, en algunos casos, hacer que el código sea más legible, aunque validar el valor exacto de la enumeración en algunos lenguajes (C #, por ejemplo) puede ser difícil.
A menudo, un parámetro booleano se agrega a la lista de parámetros como una nueva sobrecarga. Un ejemplo en .NET es:
La última sobrecarga se hizo disponible en una versión posterior de .NET Framework que la primera.
Si sabe que solo habrá dos opciones, un booleano podría estar bien. Las enumeraciones son extensibles de manera que no rompan el código antiguo, aunque las bibliotecas antiguas pueden no admitir nuevos valores de enumeración, por lo que no se puede descartar por completo el control de versiones.
EDITAR
En las versiones más recientes de C # es posible usar argumentos con nombre que, en mi opinión, pueden hacer que el código de llamada sea más claro de la misma manera que las enumeraciones. Usando el mismo ejemplo que el anterior:
fuente
Si estoy de acuerdo con que las enumeraciones son una buena manera de hacerlo, en métodos en los que tiene 2 opciones (y solo dos opciones puede tener legibilidad sin enumeración).
p.ej
Love the Enums, pero boolean también es útil.
fuente
Esta es una entrada tardía en una publicación anterior, y está tan abajo en la página que nadie la leerá, pero como nadie lo ha dicho ya ...
Un comentario en línea ayuda mucho a resolver el
bool
problema inesperado . El ejemplo original es particularmente atroz: ¡imagínese tratando de nombrar la variable en la declinación de la función! Sería algo comoPero, por el bien de ejemplo, digamos que esa es la declaración. Luego, para un argumento booleano inexplicable, puse el nombre de la variable en un comentario en línea. Comparar
con
fuente
Realmente depende de la naturaleza exacta del argumento. Si no es un sí / no o verdadero / falso, una enumeración lo hace más legible. Pero con una enumeración debe verificar el argumento o tener un comportamiento predeterminado aceptable ya que se pueden pasar valores indefinidos del tipo subyacente.
fuente
El uso de enumeraciones en lugar de booleanos en su ejemplo ayuda a que el método llame más legible. Sin embargo, este es un sustituto de mi artículo de deseo favorito en C #, argumentos nombrados en llamadas a métodos. Esta sintaxis:
sería perfectamente legible, y luego podría hacer lo que debería hacer un programador, que es elegir el tipo más apropiado para cada parámetro en el método sin tener en cuenta cómo se ve en el IDE.
C # 3.0 permite argumentos con nombre en constructores. No sé por qué no pueden hacer esto con los métodos también.
fuente
Valores booleanos
true
/false
solo. Por lo tanto, no está claro qué representa.Enum
puede tener nombre significativo, por ejemploOVERWRITE
,APPEND
, etc. enumeraciones también lo son mejor.fuente