通过数组定义类选项是一个坏习惯吗?

发布于 2024-10-10 03:39:48 字数 1574 浏览 0 评论 0原文

当我们看一下 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']);

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

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

When we take a look at Javascript frameworks like Dojo, Mootools, jQuery, JS Prototype, etc. we see that options are often defined through an array like this:

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

Is it a bad practice to implement the same idea when writing a PHP class?

An example:

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;
    }
}

The regular approach:

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;
    }
}

The advantage in the first example is that you can pass as much options as you want and the class will only extract those that it needs.

For example, this could be handy when extracting options from a JSON file:

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

This is how I do this regulary:

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

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

Is there a name for this pattern and do you think this is a bad practice?

I have left validation etc. in the examples to keep it simple.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

橙幽之幻 2024-10-17 03:39:48

有好的方面,也有坏的方面。

优点:

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

坏处:

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

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

There are good and bad aspects.

The good:

  • No need for multiple method signatures (i.e. overloading, where supported)
  • In keeping with the previous point: methods can be invoked with arguments in any order
  • Arguments can be dynamically generated, without needing to specify each one that will be present (example: you dynamically create an array of arguments based on user input and pass it to the function)
  • No need for "boilerplate" methods like setName, setThis, setThat, etc., although you might still want to include them
  • Default values can be defined in the function body, instead of the signature (jQuery uses this pattern a lot. They frequently $.extend the options passed to a method with an array of default values. In your case, you would use array_merge())

The bad:

  • Unless you properly advertise every option, your class might be harder to use because few will know what options are supported
  • It's one more step to create an array of arguments when you know ahead of time which you will need to pass
  • It's not always obvious to the user that default values exist, unless documentation is provided or they have access to the source code

In my opinion, it's a great technique. My favorite aspect is that you don't need to provide overloaded methods with different signatures, and that the signature isn't set in stone.

离旧人 2024-10-17 03:39:48

这种方法没有任何问题,特别是如果您有很多参数需要传递给构造函数。这还允许您在构造函数中为它们设置默认值和array_merge()(有点像所有 jQuery 插件)

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

如果您想要这种“模式”的实时示例,查看 symfony 框架,他们几乎在所有地方都使用它:这里是 sfValidatorBase构造函数的示例

There's nothing wrong with that approach, especially if you have a lot of parameters you need to pass to a constructor. This also allows you to set default values for them and array_merge() them inside a constructor (kinda like all jQuery plugins do)

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

If you want live examples of this "pattern", check out symfony framework, they use it almost every where: here's an example of sfValidatorBase constructor

深海蓝天 2024-10-17 03:39:48

当您给出参数名称时,它称为“命名表示法”与“位置表示法”,其中参数必须按特定顺序排列。

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

When you give the arguments names it's called "Named Notation" v.s. "Positional Notation" where the arguments must be in a specific order.

In PHP you can pass an "options" parameter to give the same effect as other languages (like Python) where you can use a genuine Named Notation. It is not a bad practice, but is often done where there is a good reason to do it (i.e. in your example or a case where there are lots of arguments and they do not all need to set in any particular order).

只怪假的太真实 2024-10-17 03:39:48

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

I don't know the name, but i really doubt it is a bad practice, since you usally use this when you wan't to declare a small o quick function or class property

虚拟世界 2024-10-17 03:39:48

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

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

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

If there are mandatory options, they should be in the constructor's parameter list. Then you add the optional options with default values.

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

If all of your options are optional, then it can be useful to create a constructor taking an array. It would be easier to create the object than with a "normal constructor" : you could provide just the options you want, while with a "normal constructor" if you want to provide $optional2, you have to provide $optional1 (even setting it to the default value).

北风几吹夏 2024-10-17 03:39:48

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

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

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

I wouldn't say its bad practice, at least if you trust the source of the data.

Another possibility would be dynamically calling the setters according to the options array key, like the following:

public function __construct($options) {
    foreach($options as $option => $value) {
        $method = 'set'.$option;
        if(method_exists($this, $method)
            call_user_func(array($this, $method, $value);
    }
}
乞讨 2024-10-17 03:39:48

为什么不两者都做呢?使用静态工厂“命名构造函数”让你的构造函数鱼与熊掌兼得

$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 感到高兴,因为他们仍然可以看到构建对象所需的内容。

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

Why not do both? Have your constructor cake and eat it too with a static factory "named constructor":

$newHello = Hello::createFromArray($options);

You first have your constructor with the options in order. Then add a static method like this to the same class:

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);
} 

This will keep new developers and IDE's happy as they can still see what it takes to construct your object.

I agree with the general attitude of the answers here in that either way is a viable solution depending on your needs and which is more beneficial for your app.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文