【问题标题】:best practice for initializing class members in php在 php 中初始化类成员的最佳实践
【发布时间】:2012-04-20 15:56:46
【问题描述】:

我的构造函数中有很多这样的代码:-

function __construct($params) {

    $this->property = isset($params['property']) ? $params['property'] : default_val;

}

一些默认值取自其他属性,这就是我在构造函数中这样做的原因。但我想它可以在 setter 中完成。

这种方法有什么优缺点,有没有更好的方法?

编辑: 我有一些依赖关系,如果$params 数组中未提供某个属性,则该值取自另一个属性,但是该其他属性可能是可选的并且具有默认值,所以初始化属性的顺序很重要。

这意味着,如果我使用 getter 和 setter,那么调用它们的顺序并不明显,因为依赖项是在 getter 中抽象出来的,而不是在构造函数中......

【问题讨论】:

    标签: php oop class constructor initialization


    【解决方案1】:

    我建议您编写适当的 getter/setter 函数,这些函数断言您正确的数据类型和验证(并包含您提到的默认值逻辑)。这些应该在您的构造函数中使用。 当设置多个相互依赖的字段时,为这些复杂数据设置一个单独的设置器似乎很好。他们究竟以哪种方式依赖?

    例如:

    // META-Config
    protected $static_default_values = array(
      "price" => 0.0,
      "title" => "foobar"
      // and so on
    );
    
    protected $fallback_getter = array(
      "price" => "getfallback_price"
    );
    
    
    // Class Logic
    public function __construct($params){
      $this->set_properties($params);
    }
    
    public set_properties($properties){
      // determines the sequence of the setter-calls
      $high_prio_fields = array("price", "title", "unimportant_field");
    
      foreach($high_prio_fields as $field){
        $this->generic_set($field, $properties[$field]);
        // important: unset fields in properties-param to avoid multiple calls
        unset($properties[$field]);
      }
    
      foreach($properties as $field => $value){
        $this->generic_set($field, $value);
      }
    }
    
    // this could also be defined within the magic-setter,
    // but be aware, that magic-functions can't be resolved by your IDE completely
    // for code-completion!
    private function generic_set($field, $value){
      // check if setter exists for given field-key
       $setter_func = "set_".$v;
       if(method_exists($this, $setter_func){
         call_user_func_array(array($this, $setter_func), array($v));
       }
       // else => just discard  :)        
    }
    
    // same comment as generic-set
    private function generic_get($field){
      // check if value is present in properties array
      if(isset($this->properties[$field]){
        return $this->properties[$field];
      }
    
      // check if fallback_getter is present
      if(isset($this->fallback_getter[$field]){
        return  call_user_func_array(array($this, $this->fallback_getter[$field]));
      }
    
      // check for default-value in meta-config
      if(isset($this->static_default_values[$field]){
        return $this->static_default_values[$field];
      }
    
      // else => fail (throw exception or return NULL)
      return null;
    }
    
    
    public function get_price(){
      // custom getter, which ovverrides generic get (if you want to)
      // custom code...
      return $this->generic_get("price");
    }
    
    private function getfallback_price(){
      return $this->properties["other_value"] * $this->properties["and_another_value"];
    }
    
    public function set_price($price){
      $price = (float) $price; // convert to correct data-type
      if($price >= 0.0){
        $this->properties["price"] = $price;
      }
      // else discard setting-func, because given parameter seems to be invalid
      // optional: throw exception or return FALSE on fail (so you can handle this on your own later)
    }
    

    更新您的编辑: 修改后的源代码应该可以解决您的所有需求(setter-funcs 的顺序,get-value 的不同解析)。

    【讨论】:

    • 这种方法对我拥有的一些依赖项会有问题,请参阅上面的编辑
    • 修改后的源代码应该可以解决您的所有需求(setter-funcs 的顺序,get-value 的不同解析)。
    • 非常感谢您的回答。我想现在我要亲吻并坚持我目前的方法。相比之下,getter 和 setter 似乎是大量代码,在我们开始遇到当前方法的问题之前,我不确定这些好处是否值得投资。
    • 不客气。但是请注意,那些通用的 getter 和 setter 最有资格在超类中实现,并且您的所有模型都应该扩展它。乍一看似乎有很多代码,但您不必在模型类中一遍又一遍地实现 代码。您只需在以后添加与您的默认行为不同的代码/方法。
    【解决方案2】:

    创建“全局可用”函数array_get

    public static function array_get($array, $property, $default_value = null) {
        return isset($array[$property]) ? $array[$property] : $default_value;
    }
    

    【讨论】:

    • 问题是关于设置没有得到:)
    • $this->property = Misc::array_get($params, 'property', 'default'); 现在你正在设置。
    • 实际上,这对于抽象出所有 $var = () ? : ; 语句来说非常简洁——我想我可能会将其合并到我当前的方法中。非常感谢!
    【解决方案3】:

    当有很多默认选项并且您需要能够覆盖它们时 - 正如您之前在 jQuery 中使用 .extend() 所看到的那样 - 我喜欢使用这种简单快捷的方法:

    class Foo {
        private $options;
    
        public function __construct($override = array()) {
            $defaults = array(
               'param1' => 'foo', 
               'param2' => ..., 
               'paramN' => 'someOtherDefaultValue');
            $this->options= array_replace_recursive($defaults, $override);
        }
    }     
    

    特别是对于启动类来说,这是一种非常简单和灵活的方式,但正如已经提到的,如果该代码将被大量使用,那么使用 getter 和设置器,特别是如果您需要在获取或设置其中一些选项时采取措施,例如如果我正确理解您的问题,则在您的情况下依赖项。

    另外请注意,您不必自己实现 getter 和 setter,在 PHP 中您可以使用 __get 和 __set 魔术方法。

    它遵循一些无用的代码,希望能提供一些想法:

    [...inside Foo...]
    public function __set($key, $value){
        switch(true){
            //option exists in this class
            case isset($this->options[$key]):
               //below check if $value is callable
               //and use those functions as "setter" handlers
               //they could resolve dependencies for example 
               $this->options[$key] = is_callable($value) ? $value($key) : $value;  
            break;
            //Adds a virtual setter to Foo. This so called 'magic' __set method is also called if the property doesn't exist in the class, so you can add arbitrary things.
            case $key === 'someVirtualSetterProp': Xyzzy::Noop($value); break;
            default: 
               try{ parent::__set($key, $value); } catch(Exception $e){ /* Oops, fix it! */ }
        }
    }
    

    请注意,在上面的示例中,我采用了不同的方法,这样混合它们通常没有意义。我这样做只是为了说明一些想法,希望您能够更好地决定什么更适合您的需求。

    【讨论】:

    • 您建议的第一种方法几乎就是我目前正在做的事情(我简化了我的问题的代码)。我不太热衷于使用魔术方法,IMO 它们使代码的可读性降低。我想我现在可能会坚持我目前的方法,也许如果项目结束时有时间我可以重构并添加 getter 和 setter。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    相关资源
    最近更新 更多