功能参数最佳实践

时间:2009-07-16 12:29:12

标签: language-agnostic function parameters

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

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

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

这导致看起来更整洁的代码,我开始倾向于私有功能,但希望其他人看到。

E.g。

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

比以下更整洁,更具可读性:

ParamD = Start(paramA, ParamB, ParamC);  
Process(ParamA, ParamD);  
Stop(ParamC);  

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

8 个答案:

答案 0 :(得分:6)

原则上使用函数访问对象字段并没有错,但是你给出的特定示例让我感到害怕,因为简化函数调用的代价是你正在模糊数据的生命周期。

要将您的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。这意味着在您调用Process()之后调用Start()可能无效。但是代码并没有告诉你。您只能通过搜索查看初始化FieldD的位置来查找。这是在寻找错误。

我的经验法则是,如果总是安全地访问该字段,则函数应该只访问对象字段。如果它是在构造时初始化的字段,那么最好,但是存储对协作者对象的引用的字段(可能随时间变化)也是可以的。

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

答案 1 :(得分:4)

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

答案 2 :(得分:2)

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

答案 3 :(得分:2)

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

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

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

有各种各样的考虑因素,最重要的是“其他开发人员最容易理解的东西”。

答案 4 :(得分:1)

通常,最好使用参数。大大提高了使用依赖注入和测试驱动设计等模式的能力。

如果它只是一种内部方法,那就不那么重要了。

答案 5 :(得分:1)

我普遍同意Mehrdad和Mufasa的评论。什么是最好的,没有硬性规定。您应该使用适合您所处理的特定方案的方法:

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

答案 6 :(得分:1)

在面向对象的语言中,通常传递依赖关系(此类将与之通信的类)和构造函数中的配置值,以及仅在函数调用中实际操作的值。

这实际上可以更具可读性。考虑具有生成和发布发票的服务的代码。可以通过多种方式进行发布 - 通过将其发送到某种集中式服务器的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作为构造函数参数,以便可以在整个过程中使用该功能。

答案 7 :(得分:0)

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

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

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

private DoTask1( string jobid, object T)
{
}

private DoTask2( string jobid, object T)
{
}