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

时间:2020-03-05 18:44:20  来源:igfitidea点击:

我将尝试使用此问题的格式进行尝试,并且我非常乐意接受有关更好地解决该问题的方法的建议。

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

基类,易于处理类属性

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

无论如何,对课程本身:

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

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

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

基本上,PropertyHandler类具有__call magic方法。然后,通过__call传入的任何以" get"或者" set"开头的方法都将路由到将值设置或者检索到关联数组中的函数。数组中的键是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的另一个很好的论据是,它真的很难记录。

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

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

解决方案

回答

我的操作方式如下:

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

回答

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

$props2->setFristName('Mark');

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

回答

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

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

回答

@布莱恩

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.

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

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!";
        }
    }
}
I'm just not satisfied with the limitations that PHP has when it comes to implicit accessor methods.

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

回答

@标记

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

回答

我一直使用类似__call的方法来处理此问题,在很多类中,它最终都像样板代码那样结束。但是,它很紧凑,并且使用反射类仅为已设置的属性添加getters / 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。

回答

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

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

回答

我不禁投入我的2美分...

我已经习惯在此庄园http://gist.github.com/351387中使用__get__set(类似于教义的用法),然后只能通过$ obj->访问属性。 var在课程外部。这样,我们可以根据需要重写功能,而不必创建巨大的__get或者__set函数,或者在子类中重写__get__set`。