我的构造函数中有很多这样的代码: -
function __construct($params) {
$this->property = isset($params['property']) ? $params['property'] : default_val;
}
某些默认值取自其他属性,这就是我在构造函数中执行此操作的原因。但我想这可以在setter中完成。
这种方法有哪些优点和缺点,是否有更好的方法?
编辑:我有一些依赖关系,如果$params
数组中没有提供属性,那么该值来自另一个属性,但是其他属性可能是可选的并且具有默认值,因此初始化属性的顺序很重要。
这意味着如果我使用了getter和setter,那么调用它们的顺序并不明显,因为依赖项在getter中抽象而不是在构造函数中...
答案 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! */ }
}
}
请注意,在上面的例子中,我用不同的方法进行了挤压,将它们混合起来通常没有意义。我这样做只是为了说明一些想法,希望你能够更好地决定什么适合你的需要。