免责声明:我是外行目前学习编程。从来没有一个项目的一部分,也不写入任何比〜500行更长的时间。

我的问题是:防御性编程违反不要重复自己的原则?假设我的防御性编程的定义是正确的(具有通话功能验证输入的,而不是相反),那不是有损于你的代码?

例如,是这样的坏:

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

int main()
{
    int input = 10;
    foo(input); //doesn't the extra logic
    foo(input); //and potentially extra calls
    foo(input); //work against you?
}   
相比,这

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

再次作为一个外行,我不知道简单的逻辑语句多少算对你尽可能表现得好,但肯定防御性编程是不利于程序或灵魂。

有帮助吗?

解决方案

这一切都归结到合同的接口提供。有两种不同的方案为这样的:输入和输出

输入 - 和由我基本上平均函数的参数 - 应由执行作为一般规则来检查

输出 - 被返回的结果 - 应该由调用者基本上是可信的,至少在我看来

如果一方打破了合同会发生什么:

所有这一切都被这个问题磨砺?例如,假设你有一个接口:

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

和该合同规定,一个空字符串将永远不会被退回(它会在最坏的情况是一个空字符串),那么它的安全做到这一点:

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

为什么呢?好吧,如果你错了和被叫返回null,则程序会崩溃。这是的实际确定的。如果某些物体违反其合同然后一般来说结果应该是灾难性的。

您的过于防御面临的风险是,你写的很多不必要的代码(可以引入更多的bug),或者你实际上可能吞咽异常掩盖一个严重的问题,你真的不应该。

当然情况可以改变这一点。

其他提示

违反DRY原则看起来像这样:

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);
    }
}

你可以看到,问题是,我们有相同的两次检查中的程序,所以如果条件的变化,我们必须在两个地方进行修改,并有机会,我们忘记了他们中的一个,引起奇怪的行为。 DRY并不意味着“不执行相同的代码两次”,但“不写相同的代码两次”

让我的状态:第一,盲目跟随一个原则是理想主义的和错误的。你需要达到你想达到的(比如,你的应用程序的安全性),这通常是更为重要的是,违反干的。故意违反原则是最常在良好的编程必要的。

一个例子:我在重要阶段双检查(例如login服务 - 第一验证输入一次调用LoginService.Login之前,然后再次内部),但有时我倾向于稍后再次除去后我确信一切外层一个工程100%,通常使用单元测试。这取决于

我从来没有得到,虽然工作了两倍以上条件检查。忘记他们完全在另一方面,通常多个幅度恶化:)

我认为防御性编程得到怎样的一个坏名声,因为它做一些事情,是一种不可取的,包括罗嗦的代码,更显著,掩盖了错误。

大多数人似乎都认为,当它遇到错误的程序失效快,但关键任务系统最好永远不会失败,而是竭尽全力保持在错误状态的脸去。

有与这种说法有问题,当然了,如何能计划,甚至是关键任务,继续当它处于不一致的状态。当然不能,真的。

您想要的是程序采取一切合理步骤,做正确的事,即使有事情有点怪。与此同时,程序应该抱怨,大声,每次遇到这样的奇状态。而在事件遇到错误是不可恢复的,应该通常会避免发出HLT指令,而是应该优雅地失败,安全关闭其系统或激活某些备份系统(如果有)。

在您的简化的例子,是的,所述第二格式是可能优选的。

然而,这并没有真正应用到更大,更复杂,也更现实的方案。

由于你永远不会提前知道哪里或如何“foo”将被使用,则需要通过验证输入保护FOO。如果输入被呼叫者验证(例如,在你的榜样“主”),则“主”需要知道的验证规则,并应用它们。

在真实世界的编程,输入验证规则可以是相当复杂的。这是不恰当的,使主叫方知道所有的验证规则和正确地应用它们。有些来电者,某个地方,是要忘记验证规则,或做错误的。因此,它更好地把“富”里面的验证,即使它会被反复调用。这个从呼叫方到被叫方,这将释放呼叫者少想“foo”的细节,并使用它更是一个抽象的,可靠的接口转移的负担。

如果你真的有一个,其中“富”会得到相同的输入多次调用模式,我建议一个包装函数,它验证一次,而侧步验证未受保护的版本:

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 */
}

像亚历克斯说,这取决于不同的情况,例如,我几乎总是在日志中的过程的每个阶段验证输入。

在其他地方,你并不需要这一切。

然而,在你给,我假设,在第二示例中,您有一个以上的输入的例子中,“引起否则这将是冗余的主叫相同功能对于相同的输入3倍,这意味着你“将不得不写状态的3倍。现在,是多余的。

如果输入总是要检查只是将其包含在功能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top