【问题标题】:Does this function have too many parameters?这个函数是不是参数太多了?
【发布时间】:2010-04-05 13:51:33
【问题描述】:

最后,我得到了这个功能。不知道正常不正常。

function user_registration($user_name, $user_email, $user_pass, $address, 
                           $city, $postalcode, $country, $phone, $mobilephone)

如何以及为什么要改进这一点?

【问题讨论】:

  • 不行,这个函数的参数太多了:function iliketurtles($a,$b,$c,$d,$e,$f,$g,$h,$i,$j, $k,$l,$m,$n,$o,$p,$q,$r,$s,$t,$u,$w,$x,$y,$z,$aa,$ab ,$ac,$ad,$ae ...

标签: php function parameters coding-style


【解决方案1】:

您可以传递一个将所有变量很好地打包在一起的数组,或者只创建一个“用户”类并通过设置器添加所有属性,最后使用专用方法进行验证:

class User {

  public function setName($name) {
    $this->name = $name;
  }

  [...]

  public function register() {

    //Validate input
    if (empty($this->name))
      $this->errors[] = "ERROR, Username must not be emtpy";

    //Add the user to the database
    //Your SQL query
    return empty($this->errors);
  }

}

$user = new User();
$user->setName("Peter");
$success = $user->register();

if (!$success)
  echo "ERRORS OCCURED: ".print_r($user->errors, true);

【讨论】:

  • 我认为用户对象不应该有责任注册自己。因此,我否决了这个解决方案。
【解决方案2】:

一种解决方案是只有一个参数,它可以包含多条数据——比如一个数组。

你的函数可以这样定义:

function user_registration(array $data) {
    // work with $data['name']
    // and $data['email']
    // ...
}

你会这样称呼它:

user_registration(array(
    'name' => 'blah',
    'email' => 'test@example.com', 
    'pass' => '123456',
    // and so on
));


好东西是:

  • 您可以轻松添加/删除“参数”
  • “参数”可以按您想要的任何顺序传递

没那么糟糕的是:

  • 在 IDE 中输入时没有提示
  • 没有文档(如 phpDoc)

【讨论】:

  • @M28:哎呀!感谢您的注意:-)
【解决方案3】:

我个人不认为它有太多参数。通过查看函数定义,您可以清楚地知道您需要什么作为输入,如果使用数组调用就不会那么明显了。

“如果它没有坏就不要修理它!”

【讨论】:

  • 我会说要么保留参数并在函数内运行检查,要么按照 FlorianH 的建议创建用户类以获得更多的 OO 设计。传递数组确实不清楚,容易传递错误的参数名(数组索引)。
【解决方案4】:

当您查看参数名称时,您不能不注意到它们可以分为三个不同的组:

User Data:    $user_name, $user_pass
Address Data: $address, $city, $postalcode, $country
Contact Data: $user_email, $phone, $mobilephone

因此,您可以申请Introduce Parameter Object

您经常会看到一组特定的参数倾向于一起传递。几种方法可以在一个类或多个类中使用该组。这样的一组类是一个数据块,可以用一个包含所有这些数据的对象来替换。将这些参数转换为对象只是为了将数据组合在一起是值得的。这种重构很有用,因为它减少了参数列表的大小,而且长的参数列表很难理解。新对象上定义的访问器也使代码更加一致,这再次使其更易于理解和修改。

如果您不想进行 OOP,您也可以将参数分组到数组中,但是您将失去所有类型的好处。我只是假设你不介意使用对象。所以,在应用重构之后,你最终会得到

function user_registration(User $user, Address $address, Contact $contact)

查看该参数列表应该会让您注意到 Address 和 Contact 可能首先属于 User,因此您可以考虑将函数签名更改为 just

function user_registration(User $user)

然后这样称呼它:

$user = new User('johndoe', 'secretsauce');
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact('jdoe@example.com', '+123 12345', '+123 54321');
user_registration($user);

我们也可以将用户名和密码设置为 Credentials 对象,然后就这样做

user_registration(new User($credentials, $address, $contact));

通过要求 ctor 中的数据,我们确保新注册的用户确实拥有所有这些信息。我们可以争论我们是否需要地址和联系人来注册用户,所以Setter injection 在这里可能就足够了:

$user = new User(new Credentials('johndoe', 'secretsauce'));
$user->setAddress(new Address('Doe Street', 'Doe Town', 12345, 'Neverland'));
$user->setContact(new Contact('jdoe@example.com', '+123 12345', '+123 54321'));
user_registration($user);

但是,user_registration 作为全局范围内的单独函数是放错了位置。通过GRASP's Information Expert principle,方法应该在具有最多信息以履行职责的对象上。这改善了Cohesion 并减少了Coupling。换句话说:

$user = new User($credentials);
$user->setAddress($address);
$user->setContact($contact);
$user->register();

现在用户类的一个问题是它包含密码。仅在针对身份验证服务对用户进行身份验证时才需要密码。我们可以争论用户名,但密码绝对不应该是用户对象的一部分。所以你应该做类似的事情

$user = new User;
$user->setAddress($address);
$user->setContact($contact);
$user->register($credentials);

register() 被调用时,它只会使用凭证来委托将新用户插入到用户存储中。但它不会将它们保留在实际的 User 实例中。

最后,您可能想添加一个Simple Factory or Builder pattern 来封装User 的创建,以简化各种实例的aggregation。或者您可能想在此处介绍Repository patternmove the register() method。不过,这超出了这个问题的范围。

【讨论】:

    【解决方案5】:

    作为一般经验法则(不是作为固定规则),任何时候您都必须问“这个函数是否有太多参数?” -- 答案是是的。你的直觉告诉你一些你的大脑还不能解决的问题。

    在这种特殊情况下,首先想到的是应该首先检查您的用户 cred.s(用户名是否已经存在?密码是否足够复杂)并且您的用户详细信息应该单独添加,可能使用一个对象或数组。

    【讨论】:

      【解决方案6】:

      一种方法是将数组作为参数传递给该函数并将所有信息放入该数组中:

      function user_registration(array $user_info)
      {
         // process $user_info;
      }
      

      【讨论】:

        【解决方案7】:

        没有任何参数的函数(方法)是最好的。一个参数的函数比两个参数的函数好。 2个参数的函数比3个参数的函数好,以此类推。

        【讨论】:

          【解决方案8】:

          @Florianh 提供了一个完美的解决方案,可以改进您的代码。有了这个评论,我想从系统设计的角度详细说明“为什么”部分。

          从面向对象的角度来看,其他对象应该能够操作属性。这就是为什么永远不应将属性定义为“公共”的原因。它们应该是“私有的”,例如:

          private var $name;
          

          这样做的原因是当其他对象会操纵这个变量时,对象的正确工作就会受到威胁。另一方面,方法可以公开定义:

          public function register() 
          

          因此,属性的操作将通过适当的方法进行。方法也可以用来评估对属性的操作的正确性。

          可能发生两种操作:读取使用获取方法保存属性的新值使用设置方法属性。

          一个好的做法是为每个类属性实现一个 get 方法。每个可以修改的属性也应该有一个合适的设置方法。

          有时最好还是不要实现 get/set 方法(例如:showData())。这是因为在特定类中使用 getter 和 setter 可能会导致性能下降。但是,这意味着在更改或实现类时,必须小心保存虚假信息,从而危及类的完整性。

          现在,考虑一下您决定只使用一个电话号码而不是同时使用电话号码和手机号码这一事实。当手机号码被弃用时,您的主程序仍然保持不变。您所要做的就是更改/删除一种方法。使用 getter 和 setter 的优点是增加了适应性可维护性

          【讨论】:

            【解决方案9】:

            我制作了类似的键数组

            $fields = array('field1', 'field2');
            function register (array $values, array $keys)
            {
                $data = array();
                foreach ($keys as $one)
                {
                    if (isset($values[$one])) $data[$one] = $values[$one];
                }
                // or you can use array functions like array_flip and after - array intersect
            }
            

            【讨论】:

              【解决方案10】:

              我会这样做

              fields=explode(",","name,surname,lastname,street,city,region,zip,country");
              user_registration($fields);
              

              因为我确信这些变量来自 $_POST

              【讨论】:

              • 你能证明为什么这是一个好的、安全、可靠的想法吗?
              • 这是一个非常糟糕的主意。一方面,您必须担心防止或转义字段内的逗号。将字段单独传递给函数要好得多 - 作为单独的变量、列表中的项目、关联数组中的项目或类的成员。
              • @Andrew 好吧,正如你所建议的,$_POST 已经是关联数组了 :)
              • @YourCommonSense 我也不明白该代码的目的。因为恕我直言,这没有任何意义。但也许你可以详细说明。
              • 我很好。尽管即使在 2 年前,这也不是正确的答案,恕我直言。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2019-10-22
              • 1970-01-01
              • 2012-08-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多