在php中初始化类成员的最佳实践

时间:2012-04-07 10:07:39

标签: php oop class constructor initialization

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

function __construct($params) {

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

}

某些默认值取自其他属性,这就是我在构造函数中执行此操作的原因。但我想这可以在setter中完成。

这种方法有哪些优点和缺点,是否有更好的方法?

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

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

3 个答案:

答案 0 :(得分:1)

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

e.g:

// 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的不同解析)。

答案 1 :(得分:0)

创建“全局可用”功能array_get

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

答案 2 :(得分:0)

当有很多默认选项并且你需要能够覆盖它们时 - 正如你之前在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引入一些对这些选项的更多控制可能不是一个坏主意。 setters,特别是如果你需要在获取或设置其中一些选项时采取行动,如果我正确理解你的问题,就像在你的情况下依赖。

另请注意,您不必自己实现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! */ }
    }
}

请注意,在上面的例子中,我用不同的方法进行了挤压,将它们混合起来通常没有意义。我这样做只是为了说明一些想法,希望你能够更好地决定什么适合你的需要。