【问题标题】:Function Parameter best practice函数参数最佳实践
【发布时间】:2009-07-16 12:29:12
【问题描述】:

我对函数参数的使用有疑问。

在过去,我总是编写代码,以便将函数所需的所有信息作为参数传入。 IE。不使用全局参数。

但是通过查看其他人的代码,没有参数的函数似乎是常态。我应该注意,这些是用于类的私有函数,并且作为参数传入的值实际上是该类的私有成员变量。

这导致代码看起来更整洁,并且我开始倾向于将其用于私人功能,但希望其他人的意见。

例如

Start();  
Process();  
Stop();  

比:

更简洁,更易读
ParamD = Start(paramA, ParamB, ParamC);  
Process(ParamA, ParamD);  
Stop(ParamC);  

从方法的角度来看,它确实打破了封装,但从类的角度来看却没有。

【问题讨论】:

    标签: language-agnostic function parameters


    【解决方案1】:

    原则上让函数访问对象字段并没有错,但是你给出的具体例子让我害怕,因为简化函数调用的代价是你混淆了数据的生命周期。

    要将您的 args 示例转换为字段,您需要:

    void Start() {
        // read FieldA, FieldB, and FieldC
        // set the value of FieldD
    }
    
    void Process() {
        // read FieldA and do something
        // read FieldD and do something
    }
    
    void Stop() {
        // read the value of FieldC
    }
    

    Start() 设置 FieldD 的副作用。这意味着在您调用Start() 之前,调用Process() 可能无效。但是代码并没有告诉你。您只能通过搜索查看FieldD 的初始化位置来查找。这是在寻找错误。

    我的经验法则是,只有在始终可以安全访问该字段的情况下,函数才应该访问该字段。最好是在构造时初始化的字段,但存储对协作对象或其他内容的引用的字段也可以随时间变化。

    但是如果调用一个函数是无效的,除非在另一个函数产生了一些输出之后,那个输出应该被传入,而不是存储在状态中。如果您将每个函数视为独立的并避免副作用,您的代码将更易于维护和理解。

    【讨论】:

    • 嘿,关于如何在共享字段和参数之间做出决定,这是一个非常好的建议。我总是有点用我的直觉 - 必须开始以这种方式更多地思考。谢谢。
    【解决方案2】:

    正如您所提到的,它们之间需要权衡取舍。总是喜欢一个而不是另一个没有硬性规则。最小化变量的范围将使它们的副作用保持在本地,代码更加模块化和可重用并且更容易调试。但是,在某些情况下,这可能是矫枉过正。如果你保持你的类很小(你应该这样做),那么共享变量通常是有意义的。但是,可能还有其他问题,例如线程安全,可能会影响您的选择。

    【讨论】:

      【解决方案3】:

      通常的做法是不将对象自己的成员属性作为参数传递给其方法:实际上,当您调用 myobject.someMethod() 时,您会隐式地将整个对象(及其所有属性)作为参数传递给方法代码。

      【讨论】:

      • OP 主要谈论私人的东西。有一个常见的替代方法是让static 实用程序方法对某些属性执行操作。在这些情况下,对象引用不会自动传递。
      【解决方案4】:

      这是您需要根据具体情况衡量的。

      例如,问问自己,如果您要在私有方法中使用参数,那么传递一个与对象中特定属性不同的值是否合理?如果没有,那么您也可以直接在方法中访问属性/字段。

      你可能会问自己这个方法会改变对象的状态吗?如果不是,那么它可能作为一个静态更好,并将其所有必需的值作为参数传递。

      各种考量,最重要的是“其他开发者最容易理解的东西”。

      【讨论】:

        【解决方案5】:

        在面向对象的语言中,通常在构造函数中传入依赖项(该类将与之通信的类)和配置值,并且仅在函数调用中实际操作的值。

        这实际上可以更具可读性。考虑您拥有生成和发布发票的服务的代码。可以通过多种方式进行发布 - 通过将其发送到某种集中式服务器的 Web 服务,或通过发送给仓库中某人的电子邮件,或者只是将其发送到默认打印机。然而,调用 Publish() 的方法通常更简单的是不知道发布如何发生的细节——它只需要知道发布顺利进行。这使您可以一次考虑更少的事情并更好地专注于问题。然后,您只需使用服务的接口(在 C# 中):

        // Notice the consuming class needs only know what it does, not how it does it
        public interface IInvoicePublisher {
          pubic void Publish(Invoice anInvoice);
        }
        

        这可以通过多种方式实现,例如:

        public class DefaultPrinterInvoicePublisher
          DefaultPrinterInvoicePublisher _printer;
          public DefaultPrinterInvoicePublisher(DefaultPrinterFacade printer) {
            _printer = printer
          }
          public void Publish(Invoice anInvoice) {
            printableObject = //Generate crystal report, or something else that can be printed
            _printer.Print(printableObject);
          }
        

        然后,使用它的代码也会将 IInvoicePublisher 作为构造函数参数,以便在整个过程中都可以使用该功能。

        【讨论】:

          【解决方案6】:

          一般来说,最好使用参数。极大地提高了使用依赖注入和测试驱动设计等模式的能力。

          不过,如果它是一种仅限内部的方法,那就没那么重要了。

          【讨论】:

            【解决方案7】:

            我大体上同意 Mehrdad 和 Mufasa 的 cmets。对于什么是最好的,没有硬性规定。您应该牢记适合您工作的特定场景的方法:

            • 代码的可读性
            • 代码的简洁性(如果将一百万零一个参数传递给一个方法,尤其是当它们是类级别的变量时,可能会变得混乱。替代方法是将参数封装成组,并创建一个包含多个值的结构,在一个对象)
            • 代码的可测试性。这在我看来很重要。我偶尔会重构代码以将参数添加到方法中,纯粹是为了提高可测试性,因为它可以实现更好的单元测试

            【讨论】:

              【解决方案8】:

              我不会将对象的状态传递给私有方法,因为该方法可以像那样访问状态。

              当从公共方法调用私有方法时,我将参数传递给私有方法,并且公共方法获取一个参数,然后将其发送给私有方法。

              Public DoTask( string jobid, object T)
              {
               DoTask1(jobid, t);
               DoTask2(jobid, t);
              }
              
              private DoTask1( string jobid, object T)
              {
              }
              
              private DoTask2( string jobid, object T)
              {
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-12-10
                • 2015-10-09
                • 2011-09-24
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多