【问题标题】:Best Practice (constructor vs setter) for creating objects with many (many) parameters?创建具有许多(许多)参数的对象的最佳实践(构造函数与设置器)?
【发布时间】:2017-05-28 05:44:26
【问题描述】:

我的代码库中有很多 POPO(普通旧 PHP 对象),其中一些包含超过 30 个字段。其中一些对象有许多必填字段,以及许多可选字段(其中一些设置了默认值。)

以下是这些类之一的简化​​版本:

Class POPO  {
    private $required;
    private $alsoRequired;
    private $defaultSet = 100;
    private $optional;
    private $alsoOptional;

    public function __construct()  {
        //some constructor code
    }

    public function setRequired($required)  {
        //validate here
        $this->required = $required;
    }

    //other setters
    ...
}

我的问题是关于最佳实践的。我想实例化对象并设置值,我有两个选择:

  1. 我可以创建一个包含非必需字段的默认值的构造函数,并为可选内容使用 setter:

    public function __construct(
        $required,
        $alsoRequired
    )  {
        $this->setRequired(1);
        $this->setAlsoRequired(2);
    }
    
    $POPO1 = new POPO(1,2);  //to instanciate new object w/ only required fields.
    $POPO1->setOptional(3);  //to set optional fields
    
  2. 我可以使用可选参数创建一个包含所有字段的构造函数:

    public function __construct(
        $required,
        $alsoRequired,
        $optional = null,
        $alsoOptional = null
    )  {
        $this->setRequired($required);
        $this->setAlsoRequired($alsoRequired);
        $this->setOptional($optional);
        $this->setAlsoOptional($alsoOptional);
    }
    
    $POPO1 = newPOPO(1,2);  //instanciate new object w/ only required fields.
    $POPO2 = newPOPO(1,2,3,4);  //instanciate object w/ optional fields.
    

在类中添加或删除参数时,这会让人感到困惑。必须更新使用该类的每个实例。选项 1 也是如此,但程度较轻。

我想到了一个第三个选项,它有一个无参数构造函数并为所有内容使用 setter,但这将允许对象处于无效状态。

所以我的问题是,这两个选项中哪一个更好?

也许我还没有想到其他更好的方法?

如何处理默认值?应该只通过 setter 来完成吗?

【问题讨论】:

  • 方法 1 听起来不错。使用关联数组移交可选属性效果很好。这减少了必须调用的函数的数量。
  • 我现在也遇到了同样的问题。具有许多字段的普通对象,其中一些(也许很多)是必需的。没有更多的答案或 cmets?

标签: php object constructor setter


【解决方案1】:

所以我的问题是,这两个选项中哪一个更好?

如果参数太多,选项 1(构造函数中的必需值,setter 中的可选值)会更具可读性。

我想到了第三个选项,即没有参数构造函数并为所有内容使用 setter,但这将允许 对象处于无效状态。

我不建议您的代码中任何时候都让对象处于无效状态。

也许我还没有想到其他更好的方法?`

class POPO
{
    protected $conf;

    public function __construct(PopoConfiguration $conf)
    {
        $this->conf = $conf;
    }

    // From now on, use $this->conf getters
}

class PopoConfiguration
{
    protected $required;
    protected $alsoRequired;
    protected $defaultSet = 100;
    protected $optional;
    protected $alsoOptional;

    public function __construct(/* required params */)
    {
        // ...
    }

    // Getters and setters
}

$popoConf = new PopoConfiguration();

// Manipulate your configuration here
$popoConf->setAlsoOptional(42);
// ...

// Instantiate POPO
$popo = new POPO($popoConf);

  • 所有必需的参数在技术上都是必需的(在 PopoConfiguration 的构造函数中)。
  • 可以在实例化新的 POPO 之前设置所有可选参数。
  • 实例化一个新的 POPO 后,它将被完全配置并处于可用状态。
  • 代码是可测试的。
  • 一个类只负责设置值(可能有某种形式的验证),一个类只负责 负责POPOing(不管它是什么)。这将使两者内部的业务逻辑更加清晰。

一个好的做法是尽可能地记录 PopoConfiguration,这样任何人都可以确切地知道他们可以(和不能)做什么来配置 POPO 对象。

如何处理默认值?应该只通过 setter 来完成吗?

如果参数很多(参数个数影响可读性),最好只通过setter来做。

【讨论】:

    【解决方案2】:

    在类中添加或删除参数时,这会让人感到困惑。

    一个类应该对扩展开放但对修改关闭 - the "O" in "SOLID"

    更简洁的方法可能是pass an array to constructor。您可以选择拥有两个单独的数组 - 一个用于必需属性,第二个用于可选属性。

    此外,如果需要,还可以使用multiple constructors

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-07
      • 2011-06-25
      • 1970-01-01
      • 2016-02-26
      • 2011-01-26
      • 2010-09-18
      • 1970-01-01
      相关资源
      最近更新 更多