用于创建具有许多(许多)参数的对象的最佳实践(构造函数与setter)?

时间:2017-01-12 23:07:37

标签: php object constructor setter

我的代码库中有很多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.
    
  3. 在类中添加或删除params时会出现混乱。必须更新使用该类的每个实例。选项1也是如此,但程度较小。

    一个thrid选项发生在我身上,没有param构造函数,并且对所有东西都使用setter,但这会让对象处于无效状态。

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

    也许还有另一种我没想到的更好的方式?

    处理默认值怎么样?这应该只通过setter来完成吗?

2 个答案:

答案 0 :(得分:0)

  

在类中添加或删除params时会出现混乱。

一个类应该是可以扩展的,但是关闭以进行修改 - the "O" in "SOLID"

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

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

答案 1 :(得分:0)

  

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

如果参数太多,则选项1(构造函数中的必需值,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对象。

  

如何处理默认值?应该只通过二传手来做到这一点吗?

如果参数很多(参数的数量会影响可读性),则最好仅通过设置器来实现。