¿Cómo nombrar un método que realiza una tarea y devuelve un valor booleano como estado?

33

Si hay un método

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

¿debería ser llamado IsStuffDone()?

El usuario puede malinterpretar ambos nombres: si el nombre es DoStuff()por qué devuelve un valor booleano? Si el nombre es, IsStuffDone()no está claro si el método realiza una tarea o solo verifica su resultado.

¿Hay alguna convención para este caso? ¿O un enfoque alternativo, ya que este se considera defectuoso? Por ejemplo, en lenguajes que tienen parámetros de salida, como C #, una variable de estado booleana podría pasarse al método como uno y el tipo de retorno del método sería void.

EDITAR: en mi problema particular, el manejo de excepciones no se puede delegar directamente a la persona que llama, porque el método es parte de una implementación de interfaz. Por lo tanto, la persona que llama no puede ser acusada de ocuparse de todas las excepciones de diferentes implementaciones. No está familiarizado con esas excepciones. Sin embargo, la persona que llama puede lidiar con una excepción personalizada StuffHasNotBeenDoneForSomeReasonExceptioncomo se sugirió en la respuesta y comentario de npinti .

Limbo exiliado
fuente
2
Depende del uso de la función y de las cosas que se están haciendo. Si es necesario, que las cosas deben ejecutarse correctamente, entonces consideraría este enfoque defectuoso, porque el usuario de la función podría perder el indicador booleano y también carece de la información proporcionada por la excepción.
Benni
77
La mayoría de las veces, lo llamaría "roto", ya que devolver un en booleanlugar de envolver o pasar la excepción es casi siempre incorrecto.
maaartinus
2
Este tipo de manejador de excepciones rara vez es una buena idea. Solo detecta excepciones específicas que esperas, no todas. Y si es posible, evite tirarlo en primer lugar, eliminando la necesidad de atraparlo.
CodesInChaos
2
Umm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? Y para responder a su pregunta sobre las excepciones, dejaría que la persona que llama las maneje o las atrape y luego arroje una excepción personalizada que significa "este método no hace su trabajo". Comparte y Disfruta.
Bob Jarvis - Restablece a Monica el
55
Para todos aquellos que están diciendo: simplemente lanza (o deja pasar) una excepción, estás haciendo suposiciones infundadas sobre cómo se usa este código. Un escenario posible es que hay un problema por resolver, con varios métodos de solución heurística que resuelven subclases cada vez mayores del problema a un costo cada vez mayor; Tendría sentido escribir algo así if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Hacer lo mismo con excepciones (¿personalizadas?) Sería inútilmente más complicado.
Marc van Leeuwen

Respuestas:

68

En .NET, a menudo tiene pares de métodos donde uno de ellos puede lanzar una excepción ( DoStuff), y el otro devuelve un estado booleano y, en una ejecución exitosa, el resultado real a través de un parámetro de salida ( TryDoStuff).

(Microsoft llama a esto el "Patrón Try-Parse" , ya que quizás el ejemplo más destacado para ello son los TryParsemétodos de varios tipos primitivos).

Si el Tryprefijo es poco común en su idioma, entonces probablemente no debería usarlo.

ne2dmar
fuente
3
+1 Así es como he visto este tipo de cosas en otros lugares. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");Es un lenguaje bastante estándar e intuitivo en mi opinión.
Karl Nicoll
12
Cabe señalar que TryDoStuffse supone que los métodos fallan limpiamente y no tienen efectos secundarios cuando devuelven falso.
Trillian
Para su información, también hay Removemétodos, por ejemplo, en .NET Framework que eliminarán un elemento de una estructura de datos (por ejemplo Dictionary) y devolverán a bool. El consumidor de la API puede decidir consumirlo o ignorarlo. Sin embargo, los errores se informan como excepciones.
Omer Iqbal
18

¿Qué pasa si simplemente lanzaste la excepción al código de llamada?

De esta manera, está delegando el manejo de excepciones a quien esté usando su código. ¿Qué pasa si en el futuro desea hacer lo siguiente?

  • Si no se lanzan excepciones, tome la acción A
  • Si (por ejemplo) FileNotFoundExceptionse lanza a, tome la acción B
  • Si se produce alguna otra excepción, tome la acción C

Si rechaza su excepción, el cambio anterior simplemente implicaría la adición de un catchbloque adicional . Si lo deja como está, necesitaría cambiar el método y el lugar desde el que se llama el método, que, dependiendo de la complejidad del proyecto, puede estar en múltiples ubicaciones.

npinti
fuente
1
¿Qué sucede si la excepción es de un tipo que no se puede delegar y se debe tratar internamente?
Limbo Exile
2
@LimboExile: Creo que aún podría tratarlo internamente y luego, tal vez lanzar otra excepción, tal vez la suya. Esto ilustraría que sucedió algo que no debería haber sucedido, pero al mismo tiempo, no expusiste lo que realmente está sucediendo, lo que supongo es por qué quieres lidiar con la excepción internamente.
npinti
66
Quizás valga la pena tener en cuenta que lanzar una excepción debería ser para un caso excepcional . Si es común o normal DoStuffque falle, lanzar una excepción para el caso de falla sería similar a controlar el flujo del programa con excepciones que son malas. Las excepciones también tienen un costo de rendimiento inherente. Si DoStufffallar es un caso poco común debido a un error, entonces definitivamente las excepciones son el camino a seguir, como sugiere @npinti.
Karl Nicoll
1
@KarlNicoll Si algo es "excepcional" es completamente subjetivo. El criterio principal debe ser si desea que el programa se bloquee si nadie hace algo al respecto, y si desea que la posibilidad de error esté presente en el tipo de función. Además, no es un hecho que las excepciones sean caras; depende del idioma y de cómo los arrojas. Las excepciones son baratas en Python, y si elimina la información de seguimiento de la pila, también pueden ser baratas en Java. Incluso si hubo un costo de rendimiento, es una optimización prematura preocuparse por eso hasta que haya realizado un perfil.
Doval
1
@Doval: estoy de acuerdo en que es subjetiva, por eso OP no debería descartar por completo la respuesta de npinti, ya que podría ser el mejor curso de acción. Mi punto era que lanzar excepciones no siempre es el mejor camino a seguir. Hay muchos casos en los que una falla no justifica lanzar una excepción y potencialmente bloquear una aplicación. Por ejemplo, si el DoStuff()método de OP se limpia después de que el código interno arroje una excepción, y las condiciones posteriores del método siguen siendo correctas, un código de retorno puede ser una mejor opción ya que el error se ha manejado.
Karl Nicoll
7

DoStuff() es suficiente, y el valor de retorno de la función debe documentarse, y no necesita mencionarlo en el nombre de la función, buscando muchas API disponibles:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )
amd
fuente
6

En Java, la API de colección define un método de agregar que devuelve un valor booleano. Básicamente, devuelve si el complemento cambió la colección. Entonces, para un List, generalmente devolverá verdadero, ya que el elemento se agregó, mientras que para un Setpodría devolver falso, si el elemento ya estaba allí, ya que Sets permite cada elemento único como máximo una vez.

Dicho esto, si agrega un elemento que no está permitido por la colección, por ejemplo, un valor nulo, el complemento arrojará un valor NullPointerExceptionfalso en lugar de devolverlo.

Cuando aplica la misma lógica a su caso, ¿por qué necesita devolver un valor booleano? Si es para ocultar una excepción, no lo hagas. Solo lanza la excepción. De esa manera puedes ver lo que salió mal. Si necesita un valor booleano, ya que podría no haberse hecho, por alguna otra razón que no sea una excepción, asigne un nombre al método DoStuff(), ya que representa el comportamiento. Hace cosas

mrjink
fuente
1

Podría fallar por diferentes razones, excepción, condiciones normales, así que usaría esto:

boolean doStuff() - returns true when successful

Importante es documentar lo que significa el booleano.

usuario136354
fuente
1

Por lo general, los métodos devuelven un OperationResult(o OperationResult<T>) y establecen la IsSuccesspropiedad en el OperationResultapropiado. Si el método 'usual' es nulo, devuelva OperationResult, si el método 'usual' devuelve un objeto, devuelva OperationResult<T>y establezca la Itempropiedad en el OperationResultapropiado. También sugeriría tener métodos en OperationResultcomo .Failed(string reason)y .Succeeded(T item). OperationResultrápidamente se convierte en un tipo familiar en sus sistemas y los desarrolladores saben cómo manejarlo.

Scott Rickman
fuente
0

Realmente depende del idioma que estés usando. Al igual que para algunos idiomas, existen convenciones y protocolos que realmente no existen en muchos idiomas o en ninguno.

Por ejemplo, en el lenguaje Objective-C y los nombres de métodos SDK de iOS / Mac OS X generalmente van en la línea de:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Como puede ver, hay un método llamado viewDidLoad. Prefiero usar este tipo de nombres cada vez que hago mis propias aplicaciones. Esto podría usarse para verificar si se suponía que algo iba a suceder como usted dijo. Creo que estás pensando demasiado en lo que nombras tus métodos. La mayoría de los métodos devuelven el estado de lo que hacen independientemente, tome el ejemplo:

En PHP, mysql_connect () devolverá falso si la conexión falla. No dice que lo hará, pero lo hace y la documentación dice que sí, por lo que no se puede malinterpretar al programador.

Gergy008
fuente
Ah, pero viewDidLoad es más una notificación de devolución de llamada. Los usuarios no lo llaman para cargar la vista. Más bien, se llama después de cargar la vista. Del mismo modo: isVisible no establece la visibilidad, sino que simplemente devuelve cuál es el valor actual. No hacer nada.
lilbyrdie
0

Me gustaría sugerir utilizar el prefijo 'Probar'. Este es un patrón bien conocido para situaciones en las que el método puede tener éxito o no. Este patrón de nombres ha sido utilizado por Microsoft en .NET Framework (por ejemplo, TryParseInt).

Pero, tal vez no se trata de nombrar. Se trata de cómo diseñar los parámetros de entrada / salida de su método.

Mi idea es que si un método tiene un valor de retorno, entonces el propósito de llamar a ese método debería ser obtener ese valor de retorno. Por lo tanto, debe evitar usar el tipo de retorno para indicar el estado de una operación.

En C #, prefiero usar un parámetro out. Usando una salida, estoy marcando el parámetro como un parámetro lateral. Especialmente que C # 6 admitirá la declaración de variable en línea.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.
000
fuente
0

Elegiría un nombre basado en la función principal del método. El hecho de que ese método devuelva un valor booleano no exige el prefijo 'Is'. Tengamos un código Java, que usa el prefijo 'Is' de forma bastante extensa. Echa un vistazo java.io.File.delete(). Regresa boolean, pero no se llama isFileDeleted(). La razón es que la función principal del método es eliminar un archivo. Alguien llama a este método con la intención de eliminar un archivo.

Lo booleano está ahí solo para comunicar el resultado del trabajo. Por otro lado, veamos java.util.Map.isEmpty(). Obviamente, llama a dicho método para averiguar si la colección está vacía. No quieres que se haga nada. Solo quiere saber cuál es el estado actual.

Así que personalmente, definitivamente me quedaría con DoStuff().

Este es un tema muy importante para mí. Muchas veces, cuando mantengo el código heredado, me encuentro con algo que personalmente llamo "método de mentira". El nombre sugiere la acción A, pero el cuerpo realiza la acción B. El ejemplo más extraño es un método getter que cambia los datos en la base de datos.

Jacek Prucia
fuente