Лучше охранять вызов метода или сам метод?

12

Я пишу заявку, и я дошел до этого:

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

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

private void GiveApples()
{
    ...
}

private void GiveBananas()
{
    ...
}

Это выглядит довольно просто. Есть некоторые условия, и если они верны, методы вызываются. Тем не менее, я подумал, что лучше сделать так:

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

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

    ...
}

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

    ...
}

Во втором случае каждый из методов защищает сам себя, поэтому даже если какой-либо из этих методов GiveApplesили GiveBananasвызывается извне SomeMethod, они будут выполняться только в том случае, если в настройках указан правильный флаг.

Это то, что я должен рассматривать как проблему?

В моем текущем контексте очень маловероятно, что эти два метода будут вызваны извне этого метода, но никто не может гарантировать это.

Никола
источник
5
Это зависит от того, нужно ли вам когда-либо вызывать GiveApples или GiveBananas без предварительной проверки. Поскольку защита связана с методом, она, вероятно, принадлежит методу.
Роберт Харви
связанный (возможно, дубликат): как могут сосуществовать
комнат

Ответы:

13

Я думаю о охранниках как о чем-то, что метод должен подчиняться. В вашем примере метод не должен давать яблоки, если Settings.GiveApples имеет значение false.

Если это так, то охранник определенно принадлежит внутри метода. Это предотвращает случайный вызов из другого пункта приложения без предварительной проверки охранников.

С другой стороны, если настройки применяются только к вызывающему методу, а другой метод где-то еще в вашем коде может выдавать GiveApples независимо от настройки, тогда это не защита и, вероятно, должно быть в вызывающем коде.

Джереми Хатчинсон
источник
5

Разместите охрану в самом методе. Потребитель GiveApples()или GiveBananas()не должен нести ответственность за управление охраной GiveApples().

С точки зрения дизайна SomeMethod()следует только знать, что ему нужны плоды, и не должно заботиться о том, что нужно приложению, чтобы его получить. Абстракция поиска фруктов становится утечкой, если SomeMethod()она отвечает за знание того, что существует глобальная настройка, которая разрешает получение определенных фруктов. Это каскадно, если ваш защитный механизм когда-либо изменяется, как теперь все методы, которые должны GetApples()или GetBananas()должны быть подвергнуты рефакторингу отдельно для реализации этой новой защиты. Также очень легко забыть попробовать и получить фрукты без этой проверки, когда вы пишете код.

В этом сценарии вы должны учитывать, как ваше приложение должно реагировать, когда настройки не позволяют вашему приложению приносить плоды.

ravibhagw
источник
4

Как правило, хорошей идеей является разделение ответственности за тестирование чего-то вроде внешних настроек и «основного бизнес-кода» GiveApples. С другой стороны, наличие функций, которые группируют вместе то, что принадлежит друг другу, также является хорошей идеей. Вы можете достичь обеих целей путем рефакторинга вашего кода следующим образом:

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()
{
    ...
}

Это дает вам больше шансов реорганизовать код GiveApplesи / или GiveBananasв отдельное место без каких-либо зависимостей от Settingsкласса. Это, очевидно, полезно, когда вы хотите вызывать те методы в модульном тесте, который не заботится ни о каком Settings.

Однако, если в вашей программе всегда неправильно, при любых обстоятельствах, даже в контексте тестирования, вызывать что-то вроде GiveApplesвне контекста, где Settings.GiveApplesсначала проверяется, и у вас сложилось впечатление, что просто предоставление функции, подобной GiveApplesбез Settingsпроверки, подвержено ошибкам , затем придерживайтесь варианта, в котором вы тестируете Settings.GiveApplesвнутри GiveApples.

Док Браун
источник