关于如何编写函数的编程风格问题

sha*_*jan 8 programming-languages coding-style user-input

所以,我今天只是编写了一些代码,并且我意识到在编程函数时,编码风格并没有太多的一致性.我主要担心的一个问题是它是否适合对其进行编码,以便检查用户的输入是否在函数的外部有效,或者只是将用户传递的值抛出到函数中并检查值是否有效在那里.让我举一个例子:

我有一个基于环境列出主机的功能,我希望能够将环境分成多个主机.所以用法的一个例子是这样的:

listhosts -e testenv -s 2 1
Run Code Online (Sandbox Code Playgroud)

这将从"testenv"中获取所有主机,将其分成两部分,并显示第一部分.

在我的代码中,我有一个函数,您可以在列表中传递它,它会根据您的参数返回一个列表列表进行拆分.但是,在我传递一个列表之前,我首先在getops过程中验证我MAIN中的参数,所以在主要的我检查以确保用户没有传递负面,我确保用户没有请求拆分成比方说,4部分,但要求显示第5部分(这将是无效的)等.

tl; dr:你会检查用户输入你是MAIN类的流的有效性,或者你会检查你的函数本身,并在有效输入的情况下返回一个有效的响应,或者返回NULL输入无效的情况?

显然这两种方法都有效,我只是想听听专家关于哪种方法更好:)感谢您提出任何意见和建议!仅供参考,我的例子是用Python编写的,但我对一般的编程答案更感兴趣,而不是语言特定的答案!

duf*_*ymo 5

我会在函数内部进行检查,以确保我期望的参数确实是我得到的.

称之为"防御性编程"或"按合同编程"或"断言检查参数"或"封装",但想法是该函数应负责检查其自身的前后条件并确保没有不变量侵犯.

如果您在函数之外执行此操作,您可能会对客户端不执行检查的情况持开放态度.一种方法不应该依赖于知道如何正确使用它的其他人.

如果合同失败,您可以抛出异常,如果您的语言支持它们,或者返回某种错误代码.

  • @Neil:抛出异常. (3认同)
  • 您可以返回错误代码,就像您在1970年编程一样.我的建议是什么?获取有例外的语言. (2认同)

Nor*_*sey 5

好问题!我的主要建议是你系统地解决问题.如果您正在设计一个函数f,以下是我对其规范的看法:

  1. 呼叫者f必须满足的绝对要求是什么?这些要求f前提条件.

  2. 什么是f它的调用者呢?当f返回时,什么是返回值,什么是机器的状态?在什么情况下f抛出异常,抛出什么异常?所有这些问题的答案构成f后置条件.

    前提条件和后置条件共同构成了与来电者f合同.只有满足前提条件的调用者才能依赖后置条件.

  3. 最后,直接关注你的问题,如果f调用者不符合前提条件会发生什么?你有两个选择:

    • 您保证停止该计划,希望通过信息提供信息.这是一个检查的运行时错误.

    • 什么都可以.也许有f一个段错误,也许内存已损坏,可能默默地返回错误的答案.这是一个未经检查的运行时错误.

    请注意此列表中没有的一些项目:引发异常或返回错误代码.如果要依赖这些行为,它们就会成为f合同的一部分.

现在我可以改写你的问题:

当呼叫者违反合同时,功能应该做什么?

在大多数类型的应用程序中,该函数应该使用检查的运行时错误来暂停程序.如果程序是需要可靠的应用程序的一部分,则应用程序应提供外部机制来重新启动应用程序,该应用程序因检查的运行时错误而停止(在Erlang代码中很常见),或者如果难以重新启动,则所有函数合同应该非常宽松,以便"糟糕的投入"仍然符合合同,但承诺总是提出例外.

在每个程序中,未经检查的运行时错误应该是罕见的.未经检查的运行时错误通常仅在性能方面是合理的,即使这样,只有当代码对性能至关重要时才是合理的.未经检查的运行时错误的另一个来源是使用不安全的语言进行编程; 例如,在C中,无法检查指向的内存是否已实际初始化.

你问题的另一个方面是

什么样的合同可以做出最好的设计?

这个问题的答案因问题域而异.因为我所做的工作都不是高可用性或安全关键,所以我使用限制性合同和大量检查的运行时错误(通常是断言失败).当你设计接口和一个大系统的合同,它是容易,如果你把合同简单,你把前提条件的限制(紧),并且你依赖检查运行时错误,当参数是"坏".

我有一个函数,你在列表中传递它,它会根据你的参数返回一个列表列表进行拆分.但是,在我传递一个列表之前,我首先在getops过程中验证我MAIN中的参数,所以在主要的我检查以确保用户没有传递负面,我确保用户没有请求拆分成比方说,4部分,但要求显示第5部分.

我认为这正是解决这个特定问题的正确方法:

  1. 您与用户的合同是用户可以说出任何内容,如果用户发出无意义的请求,您的程序将不会过度 - 它将发出合理的错误消息然后继续.

  2. 您与请求处理功能的内部合同是您只传递合理的请求.

  3. 因此,你有第三个功能,在第二个功能之外,它的作用是区分意义和无意义的行为 - 你的请求处理功能变得"有意义",用户被告知"无意义",并且所有合同都得到满足.

我的一个主要问题是它是否适合对其进行编码,以便检查用户的输入是否在函数的外部有效.

是.几乎总是这是最好的设计.事实上,可能有一个设计模式,某处有一个奇特的名字.但如果没有,经验丰富的程序员一遍又一遍地看到这一点.发生以下两件事之一:

  • 使用错误消息解析/验证/拒绝

  • 解析/验证/处理

这种设计有一种数据类型(请求)和四种功能.由于本周我正在编写大量的Haskell代码,所以我将在Haskell中给出一个例子:

data Request -- type of a request
parse    :: UserInput -> Request            -- has a somewhat permissive precondition
validate :: Request -> Maybe ErrorMessage   -- has a very permissive precondition
process  :: Request -> Result               -- has a very restrictive precondition
Run Code Online (Sandbox Code Playgroud)

当然还有很多其他方法可以做到这一点.可以在解析阶段和验证阶段检测到故障."有效请求"实际上可以由与"未经验证的请求"不同的类型表示.等等.