¿Es mejor proteger la llamada al método o el método en sí?

12

Estoy escribiendo una solicitud y llegué a este punto:

private void SomeMethod()
{
    if (Settings.GiveApples)
    {
        GiveApples();
    }

    if (Settings.GiveBananas)
    {
        GiveBananas();
    }
}

private void GiveApples()
{
    ...
}

private void GiveBananas()
{
    ...
}

Esto se ve bastante sencillo. Hay algunas condiciones y, si son ciertas, se están llamando a los métodos. Sin embargo, estaba pensando, ¿es mejor hacerlo así?

private void SomeMethod()
{
    GiveApples();
    GiveBananas();
}

private void GiveApples()
{
    if (!Settings.GiveApples)
    {
        return;
    }

    ...
}

private void GiveBananas()
{
    if (!Settings.GiveBananas)
    {
        return;
    }

    ...
}

En el segundo caso, cada uno de los métodos se protege, por lo que incluso si alguno de esos métodos GiveAppleso GiveBananasse llama desde afuera SomeMethod, se ejecutarán solo si tienen el indicador correcto en Configuración.

¿Es esto algo que realmente debería considerar como un problema?

En mi contexto actual, es muy poco probable que esos dos métodos sean llamados desde fuera de este método, pero nadie puede garantizarlo.

Nikola
fuente
55
Depende de si alguna vez necesita llamar a GiveApples o GiveBananas sin verificar primero. Como el guardia está asociado con el método, probablemente pertenece al método.
Robert Harvey
relacionado (posiblemente un duplicado): ¿Cómo pueden coexistir declaraciones de guardia y pequeñas funciones?
mosquito

Respuestas:

13

Pienso en los guardias como algo que el método debe obedecer. En su ejemplo, el método no debe dar manzanas si Settings.GiveApples es falso.

Si ese es el caso, entonces el guardia definitivamente pertenece al método. Esto evita que lo llame accidentalmente desde otro punto de su aplicación sin consultar primero a los guardias.

Por otro lado, si la configuración solo se aplica al método de llamada, y otro método en otro lugar de su código puede dar GiveApples independientemente de la configuración, entonces no es una protección y probablemente debería estar en el código de llamada.

Jeremy Hutchinson
fuente
5

Coloque el protector dentro del método mismo. El consumidor de GiveApples()o GiveBananas()no debe ser responsable de administrar a los guardias de GiveApples().

Desde el punto de vista del diseño SomeMethod(), solo debe saber que necesita fruta y no debe preocuparse por lo que su aplicación debe hacer para obtenerla. La abstracción de la recuperación de fruta se vuelve permeable si SomeMethod()es responsable de saber que existe un entorno global que permite la recuperación de cierta fruta. Esto cae en cascada si su mecanismo de protección cambia alguna vez, ya que ahora todos los métodos que necesitan GetApples()o GetBananas()necesitan ser refactorizados por separado para implementar esta nueva protección. También es muy fácil tratar de obtener fruta sin ese cheque mientras escribes el código.

Lo que debe considerar en este escenario es cómo debe reaccionar su aplicación cuando la Configuración no permite que su aplicación dé frutos.

ravibhagw
fuente
4

En general, a menudo es una buena idea separar las responsabilidades de probar algo como configuraciones proporcionadas externamente, y el "código comercial central" como GiveApples. Por otro lado, tener funciones que agrupen lo que pertenece también es una buena idea. Puede lograr ambos objetivos refactorizando su código de esta manera:

private void SomeMethod()
{
    GiveApplesIfActivated();
    GiveBananasIfActivated();
}

private void GiveApplesIfActivated()
{
    if (Settings.GiveApples)
    {
        GiveApples();
    }
}

private void GiveBananasIfActivated()
{
    if (Settings.GiveBananas)
    {
        GiveBananas();
    }
}

private void GiveApples()
{
    ...
}

private void GiveBananas()
{
    ...
}

Esto le brinda una mejor oportunidad de refactorizar el código GiveApplesy / o GiveBananasen un lugar separado sin ninguna dependencia de la Settingsclase. Obviamente, eso es beneficioso cuando desea llamar a esos métodos en una prueba unitaria que no le interesa Settings.

Sin embargo, si siempre está mal en su programa, bajo cualquier circunstancia, incluso en un contexto de prueba, llamar a algo como GiveApplesfuera de un contexto donde Settings.GiveApplesse verifica primero, y está bajo la impresión de que simplemente proporcionar una función como GiveApplessin la Settingsverificación es propensa a errores , luego se adhieren a la variante en la que se prueba Settings.GiveApplesen el interior de GiveApples.

Doc Brown
fuente