【问题标题】:Is it a bad practice to define class options through an array?通过数组定义类选项是一种不好的做法吗?
【发布时间】:2011-05-31 19:20:42
【问题描述】:

当我们查看 Dojo、Mootools、jQuery、JS Prototype 等 Javascript 框架时,我们会看到选项通常通过这样的数组定义:

dosomething('mainsetting',{duration:3,allowothers:true,astring:'hello'});

在编写 PHP 类时实现相同的想法是一种不好的做法吗?

一个例子:

class Hello {

    private $message = '';
    private $person = '';


    public function __construct($options) {

        if(isset($options['message'])) $this->message = $message;
        if(isset($options['person'])) $this->person = $person;
    }


    public function talk() {

        echo $this->person . ' says: ' . $this->message;
    }
}

常规方法:

class Hello {

    private $message = '';
    private $person = '';


    public function __construct() {}


    public function setmessage($message) {

        $this->message = $message;
    }


    public function setperson($person) {

        $this->person = $person;
    }


    public function talk() {

        echo $this->person . ' says: ' . $this->message;
    }
}

第一个示例的优点是您可以传递任意数量的选项,并且该类只会提取它需要的选项。

例如,这在从 JSON 文件中提取选项时会很方便:

$options = json_decode($options);
$hello = new Hello($options);

这就是我经常这样做的方式:

$options = json_decode($options);
$hello = new Hello();

if(isset($options['message'])) $hello->setmessage($options['message']);
if(isset($options['person'])) $hello->setperson($options['person']);

这种模式有名称吗?您认为这是一种不好的做法吗?

为了简单起见,我在示例中保留了验证等。

【问题讨论】:

  • 我不这么认为,它被广泛使用的是Drupal CMS!
  • 谢谢!很高兴知道这么大的名字正在使用它。
  • IIRC 认为 JS 语法不是一个数组,而是一个对象的“序列化”表示(它一直在例如 JSON 中使用)。
  • 它被称为统一构造函数:paul-m-jones.com/archives/1113
  • @scoates:你确定吗? PHP 对“统一构造函数”有自己的定义:php.net/manual/en/language.oop5.decon.php

标签: php class settings options


【解决方案1】:

有好有坏。

好的:

  • 不需要多个方法签名(即在支持的情况下重载)
  • 与前一点保持一致:方法可以按任何顺序使用参数调用
  • 参数可以动态生成,无需指定将出现的每个参数(例如:根据用户输入动态创建参数数组并将其传递给函数)
  • 不需要像setNamesetThissetThat 等“样板”方法,尽管您可能仍想包含它们
  • 可以在函数体中定义默认值,而不是签名(jQuery 经常使用这种模式。他们经常$.extend 将选项传递给具有默认值数组的方法。在你的情况下,你会使用array_merge())

坏处:

  • 除非您正确宣传每个选项,否则您的课程可能更难使用,因为很少有人知道支持哪些选项
  • 当您提前知道需要传递哪些参数时,这是创建参数数组的又一步
  • 对于用户来说,默认值的存在并不总是显而易见的,除非提供了文档或者他们可以访问源代码

在我看来,这是一项很棒的技术。我最喜欢的方面是您不需要提供具有不同签名的重载方法,并且签名不是一成不变的。

【讨论】:

  • +½ 用于指出需要适当的文档。 +½ 表示优点和缺点。
  • 好坏参半!我想在其中实施的项目有很好的文档,所以不应该有任何问题。
【解决方案2】:

这种方法没有任何问题,尤其是当您需要将大量参数传递给构造函数时。这也允许您在构造函数中为它们设置 default 值和array_merge() 它们(有点像所有 jQuery 插件所做的)

protected $default_params = array(
    'option1' => 'default_value'
);
public function __construct($params = array()) {
    $this->params = array_merge($this->default_params, $params);
}

如果您想要这种“模式”的实时示例,请查看 symfony 框架,他们几乎在任何地方都使用它:这里是 an example of sfValidatorBase constructor

【讨论】:

    【解决方案3】:

    当你给参数命名时,它被称为“命名符号”v.s. “位置表示法”,其中参数必须按特定顺序排列。

    在 PHP 中,您可以传递一个“选项”参数,以提供与其他语言(如 Python)相同的效果,您可以在其中使用真正的命名符号。这不是一个不好的做法,但通常在有充分理由的情况下这样做(即在您的示例中或有很多参数并且它们不需要按任何特定顺序设置的情况)。

    【讨论】:

    • +1 提到这是一种在不支持它们的语言中获取命名参数的技巧。没有多少人知道命名参数,直到他们接触到这种黑客攻击。
    【解决方案4】:

    我不知道名字,但我真的怀疑这是一个不好的做法,因为当你不想声明一个小的快速函数或类属性时,你通常会使用它

    【讨论】:

      【解决方案5】:

      如果有强制选项,它们应该在构造函数的参数列表中。然后添加带有默认值的可选选项。

      public function __construc($mandatory1, $mandatory2, $optional1="value", $optional2="value") { }
      

      如果您的所有选项都是可选的,那么创建一个采用数组的构造函数会很有用。创建对象比使用“普通构造函数”更容易:你可以只提供你想要的选项,而使用“普通构造函数”如果你想提供 $optional2,你必须提供 $optional1(甚至设置它到默认值)。

      【讨论】:

        【解决方案6】:

        我不会说这是不好的做法,至少如果你相信数据的来源的话。

        另一种可能性是根据选项数组键动态调用设置器,如下所示:

        public function __construct($options) {
            foreach($options as $option => $value) {
                $method = 'set'.$option;
                if(method_exists($this, $method)
                    call_user_func(array($this, $method, $value);
            }
        }
        

        【讨论】:

          【解决方案7】:

          为什么不两者都做?使用静态工厂“命名构造函数”让你的构造函数吃掉它
          $newHello = Hello::createFromArray($options);

          首先,您的构造函数按顺序排列选项。然后在同一个类中添加这样的静态方法:

          public static function createFromArray($options){
          
              $a = isset($options['a']) ? $options['a'] : NULL;
              $b = isset($options['b']) ? $options['b'] : NULL;
              $c = isset($options['c']) ? $options['c'] : NULL;
          
              return new Hello($a, $b, $c);
          } 
          

          这将使新开发人员和 IDE 感到高兴,因为他们仍然可以看到构建对象需要什么。

          我同意这里答案的一般态度,无论哪种方式都是可行的解决方案,具体取决于您的需求,并且对您的应用更有利。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-08-15
            • 2017-02-04
            • 2013-08-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多