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

时间:2008-08-28 12:54:10

标签: php oop

我将尝试使用这个问题的格式,我非常愿意接受有关更好的处理方法的建议。

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

base class for easy class property handling

我的想法是,人们可以在此处发布代码段或在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”方法,PropertyHandler也没有。所有这一切都在操纵数组值。

另一种情况是您的子类已经扩展了其他内容。由于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的另一个好理由是它很难记录。

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

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

8 个答案:

答案 0 :(得分: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

答案 1 :(得分:3)

@Brian

  
    
      
        

我的问题是,添加“以后更多逻辑”要求您添加适用于使用getter / setter访问的所有属性的全局逻辑,或者使用if或switch语句来评估您正在访问哪个属性,以便你可以应用特定的逻辑。

      
    
  

这不是真的。拿我的第一个例子:

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

$props = new PropTest();

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

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

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

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

我对PHP在隐式访问器方法方面的局限性不满意。

      
    
  

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

答案 2 :(得分:2)

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

$props2->setFristName('Mark');

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

答案 3 :(得分:1)

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

我喜欢你的解决方案,我为此鼓掌 - 我对PHP在隐式访问器方法方面的局限性不满意。

答案 4 :(得分:1)

@马克

但是即使你的方法需要一个新的方法声明,它有点消除了将它放在一个方法中的优势,这样你就可以添加更多的逻辑,因为要添加更多的逻辑需要方法的老式声明,无论如何。在它的默认状态下(它在它检测到的内容中给人留下深刻印象),你的技术在公共领域没有任何优势(在PHP中)。您限制访问该字段,但通过访问者方法提供全权委托,这些方法没有任何自己的限制。我不知道未经检查的显式访问者在任何语言中提供了优于公共字段的任何优势,但如果我错了,人们可以而且应该随时纠正我。

答案 5 :(得分: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。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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

我已经在这个庄园http://gist.github.com/351387中使用__get__set(类似于教义的方式),然后才通过{{1}访问属性在课堂外面。这样,您可以根据需要覆盖功能,而不是在子类中创建巨大的$obj->var__get函数,或覆盖__set__get