3

这可能是一个 JavaScript 问题,但在 Node.js 中,我通常会看到模块或方法将“选项”对象作为参数。例如,我正在谈论的内容,请查看以下从 Node.js API 文档获取的http.request()方法。

var options = {
  hostname: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST'
};

var req = http.request(options, function(res) {
  console.log('STATUS: ' + res.statusCode);
  console.log('HEADERS: ' + JSON.stringify(res.headers));
  res.setEncoding('utf8');
  res.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

req.on('error', function(e) {
  console.log('problem with request: ' + e.message);
});

// write data to request body
req.write('data\n');
req.write('data\n');
req.end();

我在许多编程语言中看到的问题之一是传递参数时没有明确什么值应该对应于什么。这是一种用于使可读性更容易的技术,还是我缺少一个基本概念?我可以在 PHP 等其他语言中合理地使用相同的技术吗?(当然使用关联数组而不是对象)

4

4 回答 4

11

您当然可以在其他语言中使用此技术。我不能说它在 JavaScript 中可能有任何结构改进,但据我所知,这个想法是减少输入参数的数量之一。

作为一个风格问题(可读性、可支持性、软件应该具有的所有良好的直观内容),这里遵循的基本“规则”是方法参数越少越好。如果不需要所有参数,则尤其如此。

考虑一个来自静态类型语言 C# 的示例:

public Widget WidgetFactory(int widgetNumber, string widgetName, bool isActive, Widget parentWidget, List<Widget> childWidgets)
{
    // parentWidget may be null if there's no parent
    // childWidgets may be empty or null for no children
    // etc.
}

对于更复杂的对象,这可能会很快变得非常难看。想象一下大量可选(可为空)参数。(如果您在 4.0 之前的 .NET 中使用过 COM 互操作,则不必想象。)还可以想象一下,如果您有多个需要这些参数的函数。再次,它变得丑陋得很快。

因此,使用的模式是将所有选项封装到另一个对象中,该对象的唯一职责是维护这些选项:

public class WidgetCreationOptions
{
    public int WidgetNumber;
    public string WidgetName;
    // etc.
}

别处...

public Widget WidgetFactory(WidgetCreationOptions options)
{
    // etc.
}

随着选项变得越来越复杂并且内部包含相互引用的逻辑,将它们抽象为自己的对象变得越来越有意义。这是向许多小的简单对象而不是较少的大对象移动的常见 OO 实践。

确实,您在此声明中是绝对正确的:

我在许多编程语言中看到的问题之一是传递参数时没有明确什么值应该对应于什么。

当涉及到许多方法参数时,有许多“代码异味”:

  • 争论太多。
  • 布尔标志作为参数(表明被调用的方法不止一件事)
  • 可空参数(如果你不需要它,为什么需要它?)

目前可能还有更多让我无法理解的东西。但是,正如您所确定的,这很难阅读:

someObject.SomeFunction(1, false, null, "this is a string", false);

而这要清楚得多:

var options = {
    widgetNumber: 1,
    isActive: false,
    widgetName: "this is a string"
};

// ... elsewhere ...

someObject.SomeFunction(options);
于 2013-01-28T19:50:49.483 回答
3

这是 JavaScript 中非常常见的模式,因为函数可以更松耦合。说起来容易得多:

doStuff( { someValue: 1, anotherValue: 2 } );

而不是:

doStuff(1, null, null, null, 2);

前一种设计有两个优点:

  1. 它更易于阅读,并且更清楚地传递了哪些值。我可以省略我不想指定的参数,而不必传入空值。我也得到了关于参数是什么的提示,因为它用一个键标记。
  2. 函数签名可以更改,添加更多密钥,而不会破坏现有代码。我可能会删除anotherValue,我可以忽略该键,而不必在函数签名中保留一个空白参数。
于 2013-01-28T19:46:04.320 回答
1

它使调用者更容易不必传递一些参数。假设端口默认为 80,方法默认为“POST”,您只需要以下代码

http.request({
  hostname: 'www.google.com',
  path: '/upload',
}, callback)

如果您切换到所有参数,您的请求函数将采用以下参数

http.request(hostname, port, path, method, callback);

在需要默认值的情况下,您需要将其称为

http.request(hostname, undefined, path, undefined, callback);

你可以看到它看起来不太漂亮。我同意这种风格的参数通常没有记录,如果没有很好的记录,它会让人更难理解如何调用它

在提供类的语言中,如 PHP、C# 和 Java,您可以创建一个类来表示 JavaScript 中的匿名对象。

在 PHP 中,您还可以使用数组来模拟 JavaScript 匿名对象。

于 2013-01-28T19:46:06.280 回答
0

我看到这种模式的一些好处:

  1. 无需记住参数的顺序
  2. 更容易使一些(或所有)参数成为可选的
  3. 在函数体本身中,当您引用参数时非常清楚。例如options.user,而不仅仅是user. 这使得错误地覆盖传递的参数变得更加困难。
于 2013-01-28T19:56:55.217 回答