PHP-初始化带有大量参数和默认值的对象的最佳方法 [英] PHP - best way to initialize an object with a large number of parameters and default values

查看:104
本文介绍了PHP-初始化带有大量参数和默认值的对象的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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




  • 制作它易于使用的类

  • 使其易于自动记录该类(即:使用phpDocumentor)

  • 对此进行优雅的编码
  • $鉴于以上所述,我不想让构造函数传递大量参数。我将传递一个包含初始化值的哈希,例如: $ foo = new Foo(array('type'=>'bar','width'=> 300,'interactive '=> false));



    在对类进行编码方面,我仍然觉得我宁愿拥有... p>

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

    ...
    }

    ...因为我相信这将有助于文档的生成(您将获得该类的属性的列表,该列表使API用户知道他们必须使用哪些选项),并且感觉到了正确的处理方式。



    但是随后您遇到了将构造函数中的传入参数映射到类变量的问题,并且在不利用符号表的情况下,您遇到了蛮力方法,我无法达到目的(尽管我愿意接受其他意见)。例如:

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

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

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

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

    但是,这似乎是我没有利用



    感谢您阅读本文的全部内容;我在这里可能要问很多,但我是PHP新手,实际上只是在寻找惯用的/优雅的方法。您的最佳做法是什么?






    附录(有关此特定班级的详细信息)



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



    根据要求,以下是部分清单一些参数(Perl代码)。您应该看到它们不能很好地映射到子类。



    该类肯定具有许多此类属性的getter和setter方法,因此用户可以重写他们;这篇文章的目的(以及原始代码的出色表现)是提供一种紧凑的方法来实例化具有已设置的必需参数的这些Form对象。

     #表单行为参数
    #--------- -----------------
    $ self-> {id}; #id和< form>的名称标签
    $ self-> {name} = webform; #legacy-替换为{id}
    $ self-> {user_id} = $ global-> {user_id}; #用于确保所有链接中都编码有用户ID。通常,这会作为{’i’}用户输入参数
    $ self-> {no_form}返回; #如果已设置,则< form>标签将被省略
    $ self-> {readonly}; #如果已设置,则整个表单将为只读
    $ self-> {autosave} =’’; #当设置为true时,对字段进行非聚焦会使字段数据立即保存
    $ self-> {scrubbed}; #如果设置为 true或非null,则在每条记录的行表单的最右边放置一个更改的单选按钮,指示已编辑一条记录。用于允许用户同时编辑多个记录并一次保存所有结果。很酷。
    $ self-> {add_rowid}; #如果设置,则表单中的每一行都将具有一个隐藏的 rowid输入字段,其中包含该记录的row_id(主要用于可擦除记录)。如果设置了清理参数,则也会自动设置此参数。请注意,要使其正常工作,SELECT语句必须提取唯一的行ID。
    $ self-> {row_id_prefix} = row_; #每行都会获得一个唯一的id,格式为id = row _ ##,其中##对应于记录的rowid。在多种形式的情况下,如果需要标识特定的行,可以将 row_前缀更改为唯一的名称。默认情况下为 row_

    $ self-> {validate_form}; #在
    $ self-> {target}格式中解析user_input并验证必填字段等; #如果指定了
    $ self-> {focus_on_field},则将目标窗口添加到表单标签中; #如果提供,则会添加< script>表单末尾的标记,将在表单加载后将焦点设置在命名字段上。
    $ self-> {on_submit}; #如果提供了
    $ self-> {ctrl_s_button_name},则将onSubmit事件处理程序添加到表单标签中; #如果提供了savebutton的名称,则会添加一个onKeypress处理程序来处理CTRL-S,以保存表单

    #表单分页参数
    #----- -----------------
    $ self-> {max_rows_per_page}; #使用printForm()方法显示完整表单时,确定一次显示在屏幕上的行数。如果为空或undef,则显示查询中的所有行,并且不生成页眉/页脚。
    $ self-> {max_pages_in_nav} = 7; #在列表列表上方和下方显示导航栏时,确定显示多少个页面链接。应该是一个奇数
    $ self-> {current_offset}; #当前显示的页面
    $ self-> {total_records}; #查询
    $ self-> {hide_max_rows_selector} =返回的记录数; #隐藏< select>标签,允许用户选择max_rows_per_page
    $ self-> {force_selected_row} =; #如果设置了此项,则对showPage()的调用也将清除表单上的rowid隐藏字段,如果未选择任何记录,则将强制显示第一条记录
    $ self-> {paging_style} = normal; #选项:紧凑

    当然,我们可以将自己拉长一些关于编程风格的辩论。但我希望避免这种情况,因为所有参与者都保持理智!这里(再次是Perl代码)是使用大量参数实例化此对象的示例。

      my $ form =新的Valz :: Webform(
    id => dbForm,
    form_name => user_mailbox_recip_list_students,
    user_input => \%params,
    user_id => ; $ params {i},
    no_form => no_form,
    selectable =>复选框,
    selectable_row_prefix =>学生,
    selected_row => ; join(,,getRecipientIDsByType('student')),
    this_page => $ params {c},
    pagesing_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;

    函数__construct($ args){
    foreach($ args as $ key => $ val){
    $ name = __。 $ key;
    if(isset($ set-> {$ name})){
    $ this-> {$ name} = $ val;
    }
    }
    }
    }

    ?>

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

     <?php 

    / **
    * @属性字符串$ type
    * @属性整数$ width
    * @属性布尔值$ interactive
    * /
    class Foo {
    private $ _instance_params = array(
    'type'=>'default_type',
    'width'=> 100,
    'interactive'=> true true
    );

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

    公共功能__get($ name)
    {
    return $ this-> _instance_params [$ name];
    }

    公共功能__set($ name,$ value)
    {
    $ this-> _instance_params [$ name] = $ value;
    }
    }

    ?>

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


    I'm designing a class that defines a highly complex object with a ton (50+) of mostly optional parameters, many of which would have defaults (eg: $type = 'foo'; $width = '300'; $interactive = false;). I'm trying to determine the best way to set up the constructor and instance/class variables in order to be able to:

    • make it easy to use the class
    • make it easy to auto-document the class (ie: using phpDocumentor)
    • code this elegantly

    In light of the above, I don't want to be passing the constructor a ton of arguments. I will be passing it a single hash which contains the initialization values, eg: $foo = new Foo(array('type'=>'bar', 'width'=>300, 'interactive'=>false));

    In terms of coding the class, I still feel like I would rather have...

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

    ...because I believe this would facilitate documentation generation (you get the list of the class' properties, which lets the API user know what 'options' they have to work with), and it "feels" like the right way to do it.

    But then you run into the problem of mapping the incoming parameters in the constructor to the class variables, and without exploiting the symbol table, you get into a "brute force" approach which to me defeats the purpose (though I'm open to other opinions). E.g.:

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

    I've considered creating a single class variable that is itself an associative array. Initializing this would be really easy then, e.g.:

    private $_instance_params = array(
        'type' => 'default_type',
        'width' => 100,
        'interactive' => true
    );
    
    function __construct($args){
        foreach($args as $key=>$value){
            $_instance_params[$key] = $value;
        }
    }
    

    But this seems like I'm not taking advantage of native features like private class variables, and it feels like documentation generation will not work with this approach.

    Thanks for reading this far; I'm probably asking a lot here, but I'm new to PHP and am really just looking for the idiomatic / elegant way of doing this. What are your best practices?


    Addendum (details about this particular Class)

    It's quite likely that this class is trying to do too much, but it is a port of an old Perl library for creating and processing forms. There's probably a way of dividing the configuration options to take advantage of inheritance and polymorphism, but it may actually be counter-productive.

    By request, here is a partial listing of some of the parameters (Perl code). You should see that these don't map very well to sub-classes.

    The class certainly has getters and setters for many of these properties so the user can over-ride them; the objective of this post (and something the original code does nicely) is to provide a compact way of instantiating these Form objects with the required parameters already set. It actually makes for very readable code.

    # 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"
    

    We can, of course, allow ourselves to be drawn into a more lengthy debate around programming style. But I'm hoping to avoid it, for the sanity of all involved! Here (Perl code, again) is an example of instantiating this object with a pretty hefty set of parameters.

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

    解决方案

    I can think of two ways of doing that. If you want to keep your instance variables you can just iterate through the array passed to the constructor and set the instance variable dynamically:

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

    When using the array approach you don't really have to abandon documentation. Just use the @property annotations in the class body:

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

    That said, a class with 50 member variables is either only used for configuration (which can be split up) or it is just doing too much and you might want to think about refactoring it.

    这篇关于PHP-初始化带有大量参数和默认值的对象的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆