¿Cómo nombrar un método que puede o no realizar una acción dependiendo de una condición?

7

Me tropiezo con este caso con cierta frecuencia, y me sorprende encontrar tan pocas discusiones similares en la web. Esta pregunta está muy relacionada. pero mi problema es que quiero un método que haga el más general "hacer X si Y" en lugar de "hacer X si es necesario" . La respuesta en ese enlace es usar el prefijo Ensure , pero esa palabra no encaja si asegurarse de que X no sea la intención del método.

El escenario que tengo en mente es este:

void mayPerformAction() {
    // Do some preparatory calculations
    // ...
    if (shouldPerform) {
        // Perform action
        // ...
    }
}

La razón por la que no estoy usando dos métodos separados ( shouldPerformAction() y performAction() ) es porque tanto la condición como la acción dependen de algunos cálculos preparatorios, que tendrían que repetirse de otra manera. Ahora, mi pregunta es: ¿cuál es el nombre más lógico y legible para el método mayPerformAction() ?

Para aclarar, es importante para la persona que llama que la acción a veces no se ejecute, de lo contrario, me parece lógico usar performAction() .

Admito que este es un tipo de problema XY, y hay varias soluciones publicadas, cada una de las cuales tiene buenos argumentos a favor y en contra. Para resumir:

  • Resuma la duda y asígnele un nombre menos detallado, por ejemplo, solo performAction() .
  • Prefiera la claridad y haga los cálculos dos veces; La diferencia de rendimiento será despreciable en muchos casos de todos modos: if (shouldPerform()) performAction() .
  • Igual que el anterior, pero almacene el resultado compartido de los cálculos en una variable global o devuélvalo (prefiero este último) para que no se desperdicien recursos.

Siento que el mejor enfoque depende de cuán 'grave' es la condición y cuán caros son los cálculos preparatorios; como tal, estoy dejando la pregunta sin contestar por ahora.

    
pregunta aviator 04.02.2018 - 22:56

6 respuestas

12

Estás atrapado en una forma estructural de pensar. Un nombre debe abstraer los detalles de la implementación. No debería ser una mano corta para ellos.

 IfBirthdayBuyCake();

es un nombre terrible. Expone los detalles de la implementación tan mal como si los hubiera escrito aquí desnudo.

Buscaría un mejor nombre y una mejor abstracción.

celebrateHoliday();

En algún lugar de él, es probable que encuentres algo como esto

if ( birthday() ) {
    buyCake();
}

y tal vez algunos otros días festivos. Cuando lo llamas, puede que no haga nada. Lo que significa que cuando lo llamas no tienes que saber si hoy es día festivo. Que el no saber te libere de ese detalle. Eso es lo que debe hacer un buen nombre.

    
respondido por el candied_orange 05.02.2018 - 00:19
2

No aconsejaría poner condicionales en los nombres de sus métodos. Si desea que el código de llamada se lea como un condicional, use un condicional:

if (cakeIsNeeded) buyCake();

O, si lo tuyo son los operadores ternarios o los cortocircuitos:

cake = cake == null ? buyCake() : cake;
cake = cake || buyCake();

De lo contrario, puede ignorar silenciosamente las llamadas repetidas, usar memoization , o lanzar excepciones en el método para lidiar con la repetición llamadas: lo que parezca más apropiado y menos sorprendente para el método en particular.

Si tienes un nombre de método que parece que repetir llamadas podría volver a realizar su (s) acción (es) pero no , una forma de vencer a la "sorpresa" es agregar un parámetro booleano similar force , skipCache o similar. (El nombre de la bandera debe ser relevante para el nombre del método y / o la lógica de salto).

Dicho todo esto, tiendo a buscar un verbo que implique lo que el llamador necesita , en lugar de lo que hace el método . En el ejemplo de la torta, la persona que llama quiere cake y no le importa mucho de dónde viene. Me parece que solo quieres getCake() o findCake() .

Ambos nombres comunican que se devolverá Cake . No le revelan a la persona que llama cómo se ubicará ese Cake . Se puede comprar, sacar del mostrador o hacer por elfos mágicos. Esos son detalles de implementación.

Una advertencia importante a todo esto: estos patrones de denominación tienden a ser altamente idiomizados. Consulte las bibliotecas internas de su idioma para ver ejemplos de cómo ellos manejan esto. Y hable con su equipo para decidir sus propios modismos internos .

    
respondido por el svidgen 05.02.2018 - 17:57
1
  

Si hay una alternativa mejor que aún evita la ejecución de código doble, no dude en avisarme :)

Si preparatory calculations es realmente costoso y calcularlo dos veces realmente perjudicaría el rendimiento (por lo que no es un caso de prematuro optimización ) Todavía separaría la condición y la acción en dos métodos porque la lectura

if (shouldPerform()) doPerformAction()

es mucho más intuitivo. Para hacer frente al cálculo del estado, puede incluir el preparatory calculations en el estado de la clase de esta manera:

public class MyClass {
    private CalculationResult preparatoryCalculationResult = null;

    public boolean shouldPerform() {
        initIfNeccessary();
        return preparatoryCalculationResult.shouldPerform();
    }

    public void doPerformAction() {
        initIfNeccessary();
        preparatoryCalculationResult.doPerformAction();
    }

    private void initIfNeccessary() {
        if (preparatoryCalculationResult == null) {
            preparatoryCalculationResult = 42; // very expensive calculation ;-)
        }
    }

}
    
respondido por el k3b 05.02.2018 - 16:35
0

Razonablemente común es "performActionIfNeeded" (un método que sabrá inteligentemente si se necesita una acción y solo la realizará en ese caso) contra "performActionIfWanted" (un método que sabrá inteligentemente que desea realizar una acción y solo realizarlo en ese caso).

    
respondido por el gnasher729 05.02.2018 - 01:54
0

Continuando:

  

Para aclarar, es importante para la persona que llama que la acción pueda   A veces no se ejecuta, de lo contrario me parece lógico usarlo.   performAction ().

Parece que la persona que llama tiene algún conocimiento de lo que va a suceder. Por lo tanto, creo que está justificado que la persona que llama pueda decidir si la acción debe realizarse o no.

Un ejemplo:

Una función debería eliminar todos los archivos que coincidan con un patrón, pero solo si su tamaño total excede algún valor. El escaneo es costoso, pero debe hacerse para verificar la condición. La acción de eliminación requiere conocimiento del escaneo para evitar que se escanee dos veces.

Simplemente dividiría esto en dos métodos:

// Returns a ScanResult containing files matching a pattern
ScanResult performScan(Pattern pat);  

// Deletes a list of files
void delete(List<File> files);

La condición ahora puede ser fácilmente externalizada:

ScanResult result = obj.performScan(...)

if(result.totalSize() > 100 * 1024) {
    obj.delete(result.getFiles());
}
    
respondido por el john16384 06.02.2018 - 14:36
0

Parte del problema es el tipo de retorno void del método original. Sería mejor devolver algún tipo de objeto de estructura de resultados al llamante para indicar diferentes grados de éxito. Esto le da a la persona que llama la información sobre lo que sucedió o no sucedió, en caso de que necesite saber pasar el mensaje a la interfaz de usuario o algo similar. También indica de forma rápida / clara al siguiente codificador que toque el código que el método llamado podría no realizar operaciones pesadas con cada llamada.

public class OperationResult
{
    public OperationState State { get; set; }
    public string OperationMessage { get; set; } // result codes, etc
    public enum OperationState { NoOperationPeformed, OperationSuccessful } // can have more if needed
}

Puede devolver toda la clase anterior, si necesita el OperationMessage (u otros datos sobre la llamada), o simplemente el enum OperationState si solo quiere indicar que se omitió el trabajo "real" de la función .

Es posible que un codificador compañero pueda asumir, a partir de ese void return, que el método siempre hace lo mismo. Devolver lo anterior podría ayudar a indicar a través de IDE intellisense que hay otras posibilidades.

    
respondido por el Graham 06.02.2018 - 19:13

Lea otras preguntas en las etiquetas