PHP - 使用大量参数和默认值初始化对象的最佳方式

标签 php class constructor instantiation idioms

我正在设计一个类,它定义了一个高度复杂的对象,其中包含大量(50 多个)大多数可选参数,其中许多参数都有默认值(例如:$type = 'foo'; $width = ' 300';$interactive = false;)。我正在尝试确定设置构造函数和实例/类变量的最佳方法,以便能够:

  • 使类(class)易于使用
  • 使自动记录类变得容易(即:使用 phpDocumentor)
  • 优雅地编码

鉴于上述情况,我不想向构造函数传递大量参数。我将向它传递一个包含初始化值的哈希值,例如:$foo = new Foo(array('type'=>'bar', 'width'=>300, 'interactive'=>false) );

在编写类代码方面,我仍然觉得我宁愿...

class Foo {
    private $_type = 'default_type';
    private $_width = 100;
    private $_interactive = true;

    ...
}

...因为我相信这会促进文档生成(你会得到类的属性列表,这让 API 用户知道他们必须使用哪些“选项”),而且它“感觉”像是正确的方法。

但是随后您遇到了将构造函数中的传入参数映射到类变量的问题,并且在不利用符号表的情况下,您进入了一种“蛮力”方法,这对我来说达不到目的(尽管我对其他意见持开放态度)。例如:

function __construct($args){
    if(isset($args['type'])) $_type = $args['type']; // yuck!
}

我考虑过创建一个单独的类变量,它本身就是一个关联数组。初始化它会很容易,例如:

private $_instance_params = array(
    'type' => 'default_type',
    'width' => 100,
    'interactive' => true
);

function __construct($args){
    foreach($args as $key=>$value){
        $_instance_params[$key] = $value;
    }
}

但这似乎我没有利用私有(private)类变量等 native 功能,并且感觉文档生成不适用于这种方法。

感谢您阅读到这里;我可能在这里问了很多,但我是 PHP 的新手,我真的只是在寻找这样做的惯用/优雅的方式。您的最佳做法是什么?


附录(关于这个特定类的详细信息)

很可能这个类试图做太多事情,但它是一个用于创建和处理表单的旧 Perl 库的端口。可能有一种方法可以划分配置选项以利用继承和多态性,但实际上可能会适得其反。

应要求,这里列出了部分参数(Perl 代码)。您应该看到这些并不能很好地映射到子类。

该类当然有许多这些属性的 getter 和 setter,因此用户可以覆盖它们;这篇文章的目的(以及原始代码所做的很好的事情)是提供一种紧凑的方法来实例化这些具有已设置的所需参数的 Form 对象。它实际上使代码非常易读。

# Form Behaviour Parameters
        # --------------------------
        $self->{id}; # the id and the name of the <form> tag
        $self->{name} = "webform"; # legacy - replaced by {id}
        $self->{user_id} = $global->{user_id}; # used to make sure that all links have the user id encoded in them. Usually this gets returned as the {'i'} user input parameter
        $self->{no_form}; # if set, the <form> tag will be omitted
        $self->{readonly}; # if set, the entire form will be read-only
        $self->{autosave} = ''; # when set to true, un-focusing a field causes the field data to be saved immediately
        $self->{scrubbed}; # if set to "true" or non-null, places a "changed" radio button on far right of row-per-record forms that indicates that a record has been edited. Used to allow users to edit multiple records at the same time and save the results all at once. Very cool.
        $self->{add_rowid}; # if set, each row in a form will have a hidden "rowid" input field with the row_id of that record (used primarily for scrubbable records). If the 'scrubbed' parameter is set, this parameter is also automatically set. Note that for this to work, the SELECT statement must pull out a unique row id. 
        $self->{row_id_prefix} = "row_"; # each row gets a unique id of the form id="row_##" where ## corresponds to the record's rowid. In the case of multiple forms, if we need to identify a specific row, we can change the "row_" prefix to something unique. By default it's "row_"

        $self->{validate_form}; # parses user_input and validates required fields and the like on a form
        $self->{target}; # adds a target window to the form tag if specified
        $self->{focus_on_field}; # if supplied, this will add a <script> tag at the end of the form that will set the focus on the named field once the form loads.
        $self->{on_submit}; # adds the onSubmit event handler to the form tag if supplied
        $self->{ctrl_s_button_name}; # if supplied with the name of the savebutton, this will add an onKeypress handler to process CTRL-S as a way of saving the form

        # Form Paging Parameters
        # ----------------------
        $self->{max_rows_per_page}; # when displaying a complete form using printForm() method, determines the number of rows shown on screen at a time. If this is blank or undef, then all rows in the query are shown and no header/footer is produced.
        $self->{max_pages_in_nav} = 7; # when displaying the navbar above and below list forms, determines how many page links are shown. Should be an odd number
        $self->{current_offset}; # the current page that we're displaying
        $self->{total_records}; # the number of records returned by the query
        $self->{hide_max_rows_selector} = ""; # hide the <select> tag allowing users to choose the max_rows_per_page
        $self->{force_selected_row} = ""; # if this is set, calls to showPage() will also clear the rowid hidden field on the form, forcing the first record to be displayed if none were selected
        $self->{paging_style} = "normal"; # Options: "compact"

当然,我们可以让自己卷入关于编程风格的更长时间的争论中。但我希望避免它,为了所有相关人员的理智!这里(再次是 Perl 代码)是使用大量参数实例化此对象的示例。

my $form = new Valz::Webform (
            id                      => "dbForm",
            form_name               => "user_mailbox_recip_list_students",
            user_input              => \%params,
            user_id                 => $params{i},
            no_form                 => "no_form",
            selectable              => "checkbox",
            selectable_row_prefix   => "student",
            selected_row            => join (",", getRecipientIDsByType('student')),
            this_page               => $params{c},
            paging_style            => "compact",
            hide_max_rows_selector  => 'true',
            max_pages_in_nav        => 5
        );

最佳答案

我可以想到两种方法。如果你想保留你的实例变量,你可以遍历传递给构造函数的数组并动态设置实例变量:

    <?php

    class Foo {
        private $_type = 'default_type';
        private $_width = 100;
        private $_interactive = true;

        function __construct($args){
            foreach($args as $key => $val) {
                $name = '_' . $key;
                if(isset($this->{$name})) {
                    $this->{$name} = $val;
                }
            }
        }
    }

    ?>

当使用数组方法时,您实际上不必放弃文档。只需在类主体中使用@property 注释即可:

<?php

/**
 * @property string $type
 * @property integer $width
 * @property boolean $interactive
 */
class Foo {
    private $_instance_params = array(
        'type' => 'default_type',
        'width' => 100,
        'interactive' => true
    );

    function __construct($args){
        $this->_instance_params = array_merge_recursive($this->_instance_params, $args);
    }

    public function __get($name)
    {
        return $this->_instance_params[$name];
    }

    public function __set($name, $value)
    {
        $this->_instance_params[$name] = $value;
    }
}

?>

也就是说,一个有 50 个成员变量的类要么仅用于配置(可以拆分),要么就是做得太多,您可能需要考虑重构它。

关于PHP - 使用大量参数和默认值初始化对象的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5967332/

相关文章:

php - 拼图 : Splitting An HTML String Correctly

class - Lua 检查表是否为 'instance'

php - 如何使用 Laravel Eloquent 返回多个关系?

php - 无法将我的 PHP 代码连接到数据库 - Android Studio

php - 加入表并查找并获取指定的行 laravel

javascript - MooTools:从元素中删除 CSS 类

c++ - 如何在类函数中打印二维数组

c++ - 派生类中基构造函数的控制,潜在的双重初始化

具有私有(private)构造函数的 Java 子类会产生编译错误

c# - 在调用方法之前将对象存储在变量中有好处吗?