8

我将尝试使用此问题的格式进行一些尝试,并且我非常愿意接受有关更好的处理方法的建议。

我不想只是在问题中转储一堆代码,所以我已经在refactormycode.

用于轻松处理类属性的基类

我的想法是人们可以在此处发布代码片段,也可以对其进行更改refactormycode并将链接发布回他们的重构。我将根据此投票并接受答案(假设有一个明确的“赢家”)。

无论如何,到班级本身:

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

public function getFirstName()
{
   return $this->firstName;
}
public function setFirstName($firstName)
{
   return $this->firstName;
}

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

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

我喜欢使用 __call 因为它会自动处理子类已经定义了“getFirstName”方法的情况。我的印象(我可能错了)是 __get 和 __set 魔术方法不这样做。

所以这里有一个例子说明它是如何工作的:

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }
}

$props = new PropTest();

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

请注意,PropTest 实际上没有“setFirstName”或“getFirstName”方法,PropTest 也没有。所做的只是操作数组值。

另一种情况是您的子类已经在扩展其他内容。由于您在 PHP 中不能拥有真正的多重继承,因此您可以让您的子类将 PropertyHandler 实例作为私有变量。您必须再添加一个功能,但事情的行为方式完全相同。

class PropTest2
{
    private $props;

    public function __construct()
    {
        $this->props = new PropertyHandler();
    }

    public function __call($method, $arguments)
    {
        return $this->props->__call($method, $arguments);
    }
}

$props2 = new PropTest2();

$props2->setFirstName('Mark');
echo $props2->getFirstName();

请注意子类如何具有一个 __call 方法,该方法只是将所有内容传递给 PropertyHandler __call 方法。


反对以这种方式处理 getter 和 setter 的另一个很好的论点是它很难记录。

事实上,基本上不可能使用任何类型的文档生成工具,因为不存在不记录的显式方法。

我现在几乎已经放弃了这种方法。这是一个有趣的学习练习,但我认为它牺牲了太多的清晰度。

4

8 回答 8

5

我这样做的方式如下:

class test {
    protected $x='';
    protected $y='';

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

    function __call($function , $args) {
        print "generic function $function\n";
        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);

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

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
于 2008-08-28T13:06:47.823 回答
3

@布莱恩

我的问题是,稍后添加“更多逻辑”需要您添加适用于使用 getter/setter 访问的所有属性的总括逻辑,或者您使用 if 或 switch 语句来评估您正在访问的属性,以便您可以应用具体逻辑。

这并不完全正确。举我的第一个例子:

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }
}

$props = new PropTest();

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

假设我需要添加一些逻辑来验证名字。我所要做的就是向我的子类添加一个 setFirstName 方法,然后自动使用该方法。

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }

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

我只是对 PHP 在隐式访问器方法方面的限制不满意。

我完全同意。我喜欢 Python 的处理方式(我的实现只是一个笨拙的抄袭)。

于 2008-08-28T14:14:57.893 回答
2

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

$props2->setFristName('Mark');

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

于 2008-08-28T13:14:11.800 回答
1

我也喜欢使用方法而不是仅仅使用公共字段,但是我对 PHP 的默认实现(使用 __get() 和 __set())或您的自定义实现的问题是您没有在每个属性上建立 getter 和 setter基础。我的问题是,稍后添加“更多逻辑”需要您添加适用于使用 getter/setter 访问的所有属性的总括逻辑,或者您使用 if 或 switch 语句来评估您正在访问的属性,以便您可以应用具体逻辑。

我喜欢你的解决方案,我为你喝彩——我只是对 PHP 在隐式访问器方法方面的限制不满意。

于 2008-08-28T13:14:25.543 回答
1

@标记

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

于 2008-08-28T14:28:13.150 回答
0

我总是用 __call 来处理这个问题,这在我的许多课程中几乎都是样板代码。但是,它很紧凑,并且使用反射类只为您已经设置的属性添加 getter/setter(不会添加新的)。简单地显式添加 getter / setter 将添加更复杂的功能。它预计是

代码如下所示:

/**
* 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;
            }
        }
    }
}

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

class MyClass {
    public $myvar = null;
}

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

echo $test->getMyvar; //echos arapaho    

反射类可能会为您提出的建议添加一些有用的东西。整洁的解决方案@Mark。

于 2008-08-28T15:02:40.510 回答
0

就在最近,我还考虑过按照您建议的方式处理 getter 和 setter(第二种方法是我最喜欢的方法,即私有 $props 数组),但我放弃了它,因为它在我的应用程序中行不通。

我正在开发一个相当大的基于 SoapServer 的应用程序,PHP 5 的soap 接口将通过soap 传输的值直接注入到关联的类中,而无需担心类中现有或不存在的属性。

于 2009-01-06T20:21:32.533 回答
0

我忍不住投入我的 2 美分...

我已经开始使用__get__set在这个庄园http://gist.github.com/351387(类似于学说的方式),然后只通过$obj->var类外部的属性访问属性。这样,您可以根据需要覆盖功能,而不是创建一个巨大的__get__set函数,或者覆盖子类中的__get和。__set

于 2010-04-17T15:01:05.213 回答