关于oop:这是处理PHP类中的getter / setter的合理方法吗?

关于oop:这是处理PHP类中的getter / setter的合理方法吗?

Is this a reasonable way to handle getters/setters in a PHP class?

我将尝试使用此问题的格式进行尝试,并且我非常乐意就更好的方法提出建议。

我不想只是在问题中转储一堆代码,所以我已将该类的代码发布在refactormycode上。

基类,可轻松处理类属性

我的想法是人们可以在此处发布代码段,也可以在refactormycode上进行更改,然后将链接发布回其重构。我将做出投票并接受一个基于此的答案(假设有明显的"获胜者")。

无论如何,要继续讲授课程本身:

我看到了很多关于getter / setter类方法的争论,最好是直接访问简单的属性变量,还是每个类都定义了显式的get / set方法,等等。我喜欢使用显式方法的想法,以防您以后需要添加更多逻辑。然后,您不必修改任何使用该类的代码。但是我讨厌有一百万个看起来像这样的函数:

1
2
3
4
5
6
7
8
public function getFirstName()
{
   return $this->firstName;
}
public function setFirstName($firstName)
{
   return $this->firstName;
}

现在我确定我不是第一个这样做的人(我希望有人可以向我建议一种更好的方法)。

基本上,PropertyHandler类具有__call magic方法。然后,通过__call传入的任何以" get"或" set"开头的方法都将路由到将值设置或检索到关联数组中的函数。数组中的键是获取或设置后调用方法的名称。因此,如果进入__call的方法是" getFirstName",则数组键是" FirstName"。

我喜欢使用__call,因为它会自动处理子类已经定义了" getFirstName"方法的情况。我的印象(我可能错了)是__get


我的操作方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class test {
    protected $x='';
    protected $y='';

    function set_y ($y) {
        print"specific function set_y\
"
;
        $this->y = $y;
    }

    function __call($function , $args) {
        print"generic function $function\
"
;
        list ($name , $var ) = split ('_' , $function );
        if ($name == 'get' && isset($this->$var)) {
            return $this->$var;
        }
        if ($name == 'set' && isset($this->$var)) {
            $this->$var= $args[0];
            return;
        }
        trigger_error ("Fatal error: Call to undefined method test::$function()");
    }
}

$p = new test();
$p->set_x(20);
$p->set_y(30);
print $p->get_x();
print $p->get_y();

$p->set_z(40);

将输出哪个(为清楚起见添加了换行符)

1
2
3
4
5
6
7
8
9
10
generic function set_x
specific function set_y

generic function get_x
20
generic function get_y
30

generic function set_z
Notice: Fatal error: Call to undefined method set_z() in [...] on line 16

@Brian

My problem with this is that adding"more logic later" requires that you add blanket logic that applies to all properties accessed with the getter/setter or that you use if or switch statements to evaluate which property you're accessing so that you can apply specific logic.



那不是真的。以我的第一个例子为例:

1
2
3
4
5
6
7
8
9
10
11
12
class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }
}

$props = new PropTest();

$props->setFirstName("Mark");
echo $props->getFirstName();

比方说,我需要添加一些逻辑来验证名字。我要做的就是将setFirstName方法添加到我的子类中,并且该方法会自动使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }

    public function setFirstName($name)
    {
        if($name == 'Mark')
        {
            echo"I love you, Mark!";
        }
    }
}

I'm just not satisfied with the limitations that PHP has when it comes to implicit accessor methods.



我完全同意。我喜欢Python的处理方式(我的实现只是它的笨拙翻版)。


是的,必须手动声明变量,但是我发现它更好,因为我担心设置器中的错字

1
$props2->setFristName('Mark');

将自动生成一个新属性(用FristName代替FirstName),这将使调试更加困难。


@马克

但是,即使您的方法也需要对方法进行全新的声明,并且它在某种程度上也消除了将其放入方法中的优势,以便您可以添加更多的逻辑,因为要添加更多的逻辑,则需要使用老式的方法声明, 反正。在默认状态下(这在检测/执行方面令人印象深刻),您的技术(在PHP中)没有提供超过公共字段的优势。您正在限制对该字段的访问,但是通过本身没有任何限制的访问器方法来实现空白。我不知道未经检查的显式访问器在任何语言上都比公共领域具有任何优势,但是如果我错了,人们可以并且应该随时纠正我。


我也喜欢拥有一些方法,而不仅仅是使用公共字段,但是我的PHP默认实现(使用__get()和__set())或您的自定义实现的问题是,您没有在每个属性的基础。我的问题是,添加"以后更多逻辑"要求您添加覆盖逻辑,该逻辑适用于使用getter / setter访问的所有属性,或者使用if或switch语句评估要访问的属性,以便您可以应用特定逻辑。

我喜欢您的解决方案,为此我为您称赞-我只是不满意PHP在隐式访问器方法方面的局限性。


我不禁投入2美分...

我已经习惯在此庄园中使用__get__set http://gist.github.com/351387(类似于教义的用法),然后只能通过在类之外。这样,您可以根据需要覆盖功能,而不必创建巨大的__get__set函数,或者在子类中覆盖__get__set


就在最近,我还考虑过按照建议的方式处理getter和setter方法(第二种方法是我的最爱,即私有的$ props数组),但是我将其丢弃了,因为它在我的应用程序中无法解决。

我正在一个基于SoapServer的大型应用程序上工作,PHP 5的soap接口将通过soap传输的值直接注入到关联的类中,而不必担心该类中现有或不存在的属性。 >


我一直通过类似__call的方式来处理此问题,在很多类中,它最终都与样板代码一样。但是,它很紧凑,并且使用反射类仅为已设置的属性添加getters / setter方法(不会添加新属性)。只需显式添加getter / setter即可添加更复杂的功能。预期为

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Handles default set and get calls
*/

public function __call($method, $params) {

    //did you call get or set
    if ( preg_match("|^[gs]et([A-Z][\\w]+)|", $method, $matches ) ) {

        //which var?
        $var = strtolower($matches[1]);

        $r = new ReflectionClass($this);
        $properties = $r->getdefaultProperties();

        //if it exists
        if ( array_key_exists($var,$properties) ) {
            //set
            if ( 's' == $method[0] ) {
                $this->$var = $params[0];
            }
            //get
            elseif ( 'g' == $method[0] ) {
                return $this->$var;
            }
        }
    }
}

将此内容添加到已声明默认属性的类中,例如:

1
2
3
4
5
6
7
8
class MyClass {
    public $myvar = null;
}

$test = new MyClass;
$test->setMyvar ="arapaho";

echo $test->getMyvar; //echos arapaho

反射类可能会在您的建议中增加一些用处。整洁的解决方案@Mark。


推荐阅读