Безопасное программирование нарушает принцип DRY?

В поддерживаемых в настоящее время версиях Symfony (2.7+) это даже проще :

return $this->redirectToRoute('default', array('year' => $year, 'month' => $month));

6
задан jkeys 7 June 2009 в 05:56
поделиться

6 ответов

Все сводится к контракту , который предоставляет интерфейс. Для этого есть два разных сценария: входы и выходы.

Входы - и под этим я в основном имею в виду параметры функций - должны проверяться реализацией как общее правило.

Выходы - возвращаемые результаты - - вызывающий должен в основном доверять, по крайней мере, на мой взгляд.

Все это сдерживается вопросом: что произойдет, если одна из сторон нарушит контракт? Например, допустим, у вас есть интерфейс:

class A {
  public:
    const char *get_stuff();
}

, и этот контракт определяет, что пустая строка никогда не будет возвращена (в худшем случае это будет пустая строка), тогда это безопасно:

A a = ...
char buf[1000];
strcpy(buf, a.get_stuff());

Почему? Что ж, если вы ошибаетесь и вызываемый объект возвращает ноль, тогда программа выйдет из строя. Это на самом деле нормально .

6
ответ дан 8 December 2019 в 04:55
поделиться

Violating the DRY principle looks like that:

int foo(int bar)
{
    if (bar != /*condition*/)
    {
        //code, assert, return, etc.
    }
}

int main()
{
    int input = 10;
    if (input == /*condition*/)
    {
       foo(input);
       foo(input);
       foo(input);
    }
}

as you can see, the problem is that we have the same check twice in the program, so if the condition changes, we must modify it at two places, and chances are that we forget one of them, causing strange behaviour. DRY doesn't mean "don't execute the same code twice", but "don't write the same code twice"

9
ответ дан 8 December 2019 в 04:55
поделиться

Позвольте мне сначала заявить, что слепое следование принципу идеалистично и НЕПРАВИЛЬНО. Вам нужно добиться того, чего вы хотите достичь (например, безопасности вашего приложения), что обычно намного важнее, чем нарушение DRY. В ХОРОШЕМ программировании чаще всего необходимы преднамеренные нарушения принципов.

Пример: я делаю двойные проверки на важных этапах (например, LoginService - сначала проверяю ввод один раз перед вызовом LoginService.Login, а затем снова внутри), но иногда я склонен удалите внешний снова позже, когда я убедился, что все работает на 100%, обычно с помощью модульных тестов. Это зависит от обстоятельств.

Я бы никогда не стал беспокоиться о проверке двойных условий. С другой стороны, их полное забвение обычно на несколько величин хуже :)

Вам нужно добиться того, чего вы хотите достичь (например, безопасности вашего приложения), что обычно намного важнее, чем нарушение DRY. В ХОРОШЕМ программировании чаще всего необходимы преднамеренные нарушения принципов.

Пример: я делаю двойные проверки на важных этапах (например, LoginService - сначала проверяю ввод один раз перед вызовом LoginService.Login, а затем снова внутри), но иногда я склонен удалите внешний снова позже, когда я убедился, что все работает на 100%, обычно с помощью модульных тестов. Это зависит от обстоятельств.

Я бы никогда не стал беспокоиться о проверке двойных условий. С другой стороны, их полное забвение обычно на несколько величин хуже :)

Вам нужно добиться того, чего вы хотите достичь (например, безопасности вашего приложения), что обычно намного важнее, чем нарушение DRY. В ХОРОШЕМ программировании чаще всего необходимы преднамеренные нарушения принципов.

Пример: я делаю двойные проверки на важных этапах (например, LoginService - сначала проверяю ввод один раз перед вызовом LoginService.Login, а затем снова внутри), но иногда я склонен удалите внешний снова позже, когда я убедился, что все работает на 100%, обычно с помощью модульных тестов. Это зависит от обстоятельств.

Я бы никогда не стал беспокоиться о проверке двойных условий. С другой стороны, их полное забвение обычно на несколько величин хуже :)

Пример: я провожу двойную проверку на важных этапах (например, LoginService - сначала проверяю ввод один раз перед вызовом LoginService.Login, а затем снова внутри), но иногда я склонен снова удалять внешний, после того, как убедился, что все работает на 100%, обычно с использованием модульных тестов. Это зависит от обстоятельств.

Я бы никогда не стал беспокоиться о проверке двойных условий. С другой стороны, их полное забвение обычно на несколько величин хуже :)

Пример: я провожу двойную проверку на важных этапах (например, LoginService - сначала проверяю ввод один раз перед вызовом LoginService.Login, а затем снова внутри), но иногда я склонен снова удалять внешний, после того, как убедился, что все работает на 100%, обычно с использованием модульных тестов. Это зависит от обстоятельств.

Я бы никогда не стал беспокоиться о проверке двойных условий. С другой стороны, их полное забвение обычно на несколько величин хуже :)

4
ответ дан 8 December 2019 в 04:55
поделиться

Я думаю, что защитное программирование получает плохую репутацию, поскольку оно делает некоторые вещи, которые отчасти нежелательны, в том числе многословный код и, что более важно, скрывает ошибки.

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

Конечно, есть проблема с этим утверждением: как программа, даже критически важная, может продолжаться, когда она находится в несогласованном состоянии. Конечно, не может.

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

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

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

3
ответ дан 8 December 2019 в 04:55
поделиться

In your simplified example, yes, the second format is probably preferable.

However, that doesn't really apply to larger, more complex, and more realistic programs.

Because you never know in advance where or how "foo" will be used, you need to protect foo by validating the input. If the input is validated by the caller (eg. "main" in your example) then "main" needs to know the validation rules, and apply them.

In real-world programming, the input validation rules can be fairly complex. It is not appropriate to make the caller know all the validation rules and apply them properly. Some caller, somewhere, is going to forget the validation rules, or do the wrong ones. So its better to put the validation inside "foo", even if it will get called repeatedly. This shifts the burden from the caller to the callee, which frees the caller to think less about the details of "foo", and use it more as an abstract, reliable interface.

If you truly have a pattern where "foo" will get called multiple times with the same input, I suggest a wrapper function that does the validation once, and an unprotected version that side-steps the validation:

void RepeatFoo(int bar, int repeatCount)
{
   /* Validate bar */
   if (bar != /*condition*/)
   {
       //code, assert, return, etc.
   }

   for(int i=0; i<repeatCount; ++i)
   {
       UnprotectedFoo(bar);
   }
}

void UnprotectedFoo(int bar)
{
    /* Note: no validation */

    /* do something with bar */
}

void Foo(int bar)
{
   /* Validate bar */
   /* either do the work, or call UnprotectedFoo */
}
1
ответ дан 8 December 2019 в 04:55
поделиться

Как сказал Алекс, это зависит от ситуации, например, я почти всегда проверяю ввод на каждом этапе процесса входа в систему.

В других случаях вам не нужно все это.

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

Если ввод ВСЕГДА нужно проверять, просто включите его в функцию.

1
ответ дан 8 December 2019 в 04:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: