构造函数与太多的获取和设置

发布于 2024-11-25 07:25:57 字数 451 浏览 0 评论 0原文

我拥有 18 处房产,创纪录。

在将该类提交到数据库之前,所有 18 个属性都必须具有经过验证的数据。

因为我正在 OOP 化一个工作程序 Web 应用程序,所以我进行了这种倒退。

首先,我讨论了修改现有记录的工作流程。当时,将所有 18 个属性放入 __construct 方法并避免 setter 的垃圾负载是有意义的。一个单独的加载器类处理数据库业务,并且可以返回单个对象或记录对象数组。一切顺利。

但是后来是时候解决新的记录创建工作流程了,突然我需要实例化一个空记录,除了我的记录构造函数是一个饥饿的野兽,需要 18 个参数......

所以你剥离构造函数?但随后我必须添加 18 个 setter,并在每次我想要处理现有记录时调用它们……

这似乎没有多大改进! :-/

真正的程序员如何处理这个问题? (我只是一个小小爱好者……)

I have a record class with 18 properties.

Before that class can be submitted to the database, all 18 properties must have validated data.

Because I'm OOP-ifying a working procedural webapp, I went about this sort of backwards.

First I addressed workflow for modifying existing records. At the time, it made sense to throw all 18 properties into the __construct method and avoid craploads of setters. A separate loader class handles the dbase business and can return either single objects or an array of record objects. That all worked ok.

But then it came time to address the new record creation workflow, and suddenly I needed to instantiate an empty record, except my record constructor is a hungry beast that wants 18 parameters...

...so you strip the constructor? But then I'd have to add 18 setters and call them all each time I want to work with an existing record...

doesn't seem like much of an improvement! :-/

How do real programmers handle this? (I'm just a weenie hobbyist...)

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

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

发布评论

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

评论(5

请持续率性 2024-12-02 07:25:57

默认参数都是一个选项,但是如果您只想使用第一个和最后一个参数,则必须填写大量空值。

再说一遍,您可以进行数组循环:

private $prop1;
private $prop2;
// more properties here.

function __construct( array $props ) // `array` here is for type-hinting.
{
    foreach( array( 'prop1', 'prop2' /*, all of the props for this object */
             as $property )
    {
        // basically, this will assign all of the properties as they exist in the
        // props array
        if( isset( $props[ $property ] ) )
            $this->$property = $props[ $property ];
    }
}

或者,如果您想保留旧的构造函数签名:

function __construct( $prop1, $prop2 = NULL, $prop3 = NULL /* ... */ ) 
{
    if( is_array( $prop1 ) )
    {
         $this->array_prop_assignment( $prop1 );
    }
    else
    {
        $args = func_get_args();
        // this ensures that anything which is passed to the constructor
        // will go to the "new" old constructor
        call_user_func_array( array( $this, 'param_prop_assignment' ), $args );
    }
}

function param_prop_assignment( $prop1, $prop2 /* ... */ )
{
    //your old constructor can go here.
}

function array_prop_assignment( array $props )
{
    // foreach example above would go here.
}

新版本还为您提供了简单的选项:

$k = new DataClass(); // no idea what the real class name is.
$k->param_prop_assignment( 1, 2, 3 /* ... */ );

Either default arguments is one option, but then you have to fill out a large number of null's if you only want to use, say, the first and last.

Then again, you could do array looping:

private $prop1;
private $prop2;
// more properties here.

function __construct( array $props ) // `array` here is for type-hinting.
{
    foreach( array( 'prop1', 'prop2' /*, all of the props for this object */
             as $property )
    {
        // basically, this will assign all of the properties as they exist in the
        // props array
        if( isset( $props[ $property ] ) )
            $this->$property = $props[ $property ];
    }
}

Or, if you wanted to keep your old constructor signature:

function __construct( $prop1, $prop2 = NULL, $prop3 = NULL /* ... */ ) 
{
    if( is_array( $prop1 ) )
    {
         $this->array_prop_assignment( $prop1 );
    }
    else
    {
        $args = func_get_args();
        // this ensures that anything which is passed to the constructor
        // will go to the "new" old constructor
        call_user_func_array( array( $this, 'param_prop_assignment' ), $args );
    }
}

function param_prop_assignment( $prop1, $prop2 /* ... */ )
{
    //your old constructor can go here.
}

function array_prop_assignment( array $props )
{
    // foreach example above would go here.
}

The new version also gives you the option to simply:

$k = new DataClass(); // no idea what the real class name is.
$k->param_prop_assignment( 1, 2, 3 /* ... */ );
自由如风 2024-12-02 07:25:57

在任何类型的函数中填充 18 个参数(包括构造函数)都是不好的。当您几个月甚至几天后查看代码时,您将永远不会记住正确的顺序。此外,正如您所经历的,当您需要扩展课程时,这很困难。

这就是为什么我通常更喜欢使用 getter 和 setter 进行课程。是的,这需要更多的打字,但是使用 getter 和 setter,用户可以轻松查看他们可以获取和设置哪些属性,而且 getter 和 setter 是 IDE 的自动完成友好型。

现在,下一个问题是您不想在从数据库读取现有记录时一直调用 setter 吗?你说你有一个Loader类,难道你不能把所有对setter的调用集中在Loder类中吗?

class Loader{
  public function getMyObject(){
    $dbData = $this->getDataFromDB();
    $myObj = $this->createMyObjectFromDbData($dbData);  
    return $myObj;
  }

  private createMyObjectFromDbData($dbData){
    $myObj = new MyObject();
    /* 18 setter calls */

    return $myObj;
  }
}

因此,当您想要使用现有的方法时,只需调用 Loader.getMyObject();

如果您不想在 createMyObjectFromDbData 中键入所有 18 个 setter 调用,那么只要您的 setter 遵循一些命名约定,您就可以执行

for ($dbData as $columnName => $columnValue){
  $propertyName = $columnName;
  $propertyValue = $columnValue;
  $myObj->set{$columnName}($propertyValue); /* you can't do this in java */
}

您可能还需要添加一个 validate 方法来验证对象中的所有属性,以便您可以在提交要插入的对象之前调用该方法数据库。

Stuffing 18 parameters in any sort of function is (constructor included) is bad. You'll never remember the correct order when you look at your code few months, or even few days from now on. Further more, as you experienced, when you need to extend the class it's hard.

That's why I usually prefer having classes with getters and setters. Yes it's more typing, but with getter and setters users can easily see what properties they can get and set, plus getter and setters are IDE's auto-complete friendly.

Now, the next problem is you don't want to call setters all the time when you read existing record from the database? You said you have a Loader class, can't you just centralize all the calls to setters in the Loder class?

class Loader{
  public function getMyObject(){
    $dbData = $this->getDataFromDB();
    $myObj = $this->createMyObjectFromDbData($dbData);  
    return $myObj;
  }

  private createMyObjectFromDbData($dbData){
    $myObj = new MyObject();
    /* 18 setter calls */

    return $myObj;
  }
}

So when you want to work with existing you can simply call Loader.getMyObject();

If you don't feel like typing out all the 18 setter calls in createMyObjectFromDbData, then as long as your setter follows some naming convention you can do

for ($dbData as $columnName => $columnValue){
  $propertyName = $columnName;
  $propertyValue = $columnValue;
  $myObj->set{$columnName}($propertyValue); /* you can't do this in java */
}

You may also want to add a validate method to validate all the properties in the object so you can call that method before you submit it to be inserted into database.

回忆凄美了谁 2024-12-02 07:25:57

您可以将它们链接到构造函数。在哪里执行此操作...

$Record = Record()->Name('Mark')->Location('A-Town, NY')->Phone('123-345-6789' );

您可以通过创建一个与类同名的函数来返回类的新实例来实现此目的。

function Record() {
    return new Record;
}

class Record {
    private $Name;
    private $Location;
    private $Phone;

    public function __get($property) {
        return (isset($this->$property)) ? $this->$property : FALSE;
    }

    public function &__call($property, $arguments)
    {
        if (property_exists($this, $property))
            $this->$property = array_shift($arguments);
        return $this;
    }
}

$FilledRecord = Record()->Name('Mark')->Location('A-Town')->Phone('123-456-7890');
$EmptyRecord = Record();

print_r($FilledRecord);
print_r($EmptyRecord);

如果您需要验证某些数据,可以稍后添加该功能。

You can chain them to the constructor. Where you do this ...

$Record = Record()->Name('Mark')->Location('A-Town, NY')->Phone('123-345-6789');

You can do this by making a function that has the same name as your class that returns a new instances of your class.

function Record() {
    return new Record;
}

class Record {
    private $Name;
    private $Location;
    private $Phone;

    public function __get($property) {
        return (isset($this->$property)) ? $this->$property : FALSE;
    }

    public function &__call($property, $arguments)
    {
        if (property_exists($this, $property))
            $this->$property = array_shift($arguments);
        return $this;
    }
}

$FilledRecord = Record()->Name('Mark')->Location('A-Town')->Phone('123-456-7890');
$EmptyRecord = Record();

print_r($FilledRecord);
print_r($EmptyRecord);

If you need to validate some data, you can just add the function on later.

鹤仙姿 2024-12-02 07:25:57

在完美的世界中,您的对象将允许您使用构造函数或设置器来指定值。为了简化事情,您可以做的是提供一个构造函数,它只接受 18 个值的子集,并将其余值设置为默认值。但这假设存在有意义的默认值,但情况可能并非如此。

In a perfect world your object would allow you to specify values using either the constructor or setters. What you could do to simplify things is provide a constructor that only accepts a subset of the 18 values and sets the rest to default values. But that assumes that there are default values that make sense, which may not be the case.

往事风中埋 2024-12-02 07:25:57

如果一个对象有很多属性,并且不想将它们全部包含在构造函数中,则可以使用 setter 返回对象本身来编写稍微更具可读性的代码:

MyClass obj = (new MyClass())
    .setName(name)
    .setLocation(location)
    ...
    .setPhone(phone)

同样,如果所有值都需要要在获得有效对象之前进行验证,可以使用构建器对象来执行相同的操作。基本上,您创建一个构建器对象,设置值,然后告诉构建器创建实际的对象。然后,它可以在构造对象的实际实例之前对所有值进行验证(包括使用多个字段的验证)。

MyClass obj = (new MyClassFactory())
    .setName(name)
    .setLocation(location)
    ...
    .setPhone(phone)
    .construct();

If you have a lot of properties for an object and you don't want to include them all in the constructor, you can use setters return the object itself to make slightly more readable code:

MyClass obj = (new MyClass())
    .setName(name)
    .setLocation(location)
    ...
    .setPhone(phone)

Along the same lines, if all of the values need to be validated before you have a valid object, you can use a builder object to do the same thing. Basically, you create a builder object, set the values, and then tell the builder to create the actual object. It can then do the validation of all the values (including validation that uses multiple fields) right before it constructs the actual instance of the object.

MyClass obj = (new MyClassFactory())
    .setName(name)
    .setLocation(location)
    ...
    .setPhone(phone)
    .construct();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文