PHP面向对象编程中有很多的setter和getter方法。

13
我需要创建大约5-7个类,每个类都将包含许多成员(假设每个类将包含20个成员)。我可以使用公共访问权限来创建它们,例如:
class A {
    public $myPropertyOne = '';
    public $myPropertyTwo = '';
    ...
}

我偏爱将这些成员设为私有,并为每个属性创建get/set方法。例如:

class A {
    private $myPropertyOne = '';
    private $myPropertyTwo = '';

    public function getMyPropertyOne() {
            return $this->myPropertyOne;
    }

    public function setMyPropertyOne($myPropertyOne) {
            $this->myPropertyOne = $myPropertyOne;
    }

    public function getMyPropertyTwo() {
            return $this->myPropertyTwo;
    }

    public function setMyPropertyTwo($myPropertyTwo) {
            $this->myPropertyTwo = $myPropertyTwo;
    }
}

考虑到一个类会有20个属性,我还需要添加40个方法。我担心这会减慢脚本的速度并且需要更多的内存(请记住,我将拥有几个这样的类)。

另一个解决方案可能是使用magic functions __set、__get,但我不想这么做,因为开发IDE中的代码完成将不建议对我很重要的属性。

如果这是一种编译语言(如C++),我就没有问题,并且会使用getter、setter的解决方案,但由于PHP是解释性语言,我希望我的脚本使用的RAM尽可能少,速度尽可能快。

谢谢你们提前的帮助,对于这个问题的任何想法都会非常感激!


我的看法

谢谢大家的回答,我想分享一下我的观点,以便有人寻找答案。

我不能完全同意那些说你不应该关心性能的人,因为我认为这是一个重要因素(至少对我来说),当我们处理解释性语言(如PHP)时,我们始终必须考虑内存和速度(这让我想起了我为DOS开发系统应用程序的时候,呵呵:)),而且你总是会受到CPU性能和总RAM数量的限制,因此如果你可以节省额外的字节,就会感到高兴。在PHP开发中,你有同样的情况,无论你添加多少服务器,用户数量都会更高,所以你必须决定是否要遵循经典/安全/适当的方法,还是避免这些并在速度或内存上获得一些收益。

所以...我认为在这里最好的方法是对所有成员使用公共访问,并避免使用getter/setter来访问所有属性,对于需要数据验证或初始化的属性,使用私有访问和get/set方法来设置值之前进行操作。

例如:

class B {
    public $myPropertyOne = '';
    public $myPropertyTwo = '';
    private $myPropertyThree = array();


    public function getMyPropertyThree() {
        return $this->myPropertyThree;
    }

    public function setMyPropertyThree($val) {
        if(!is_array($val)) {
            return;
        }

        $this->myPropertyThree = $val;
    }
}

感谢您抽出时间阅读我的问题!


14
这是一种微小的优化,你永远不需要担心它。 - Karoly Horvath
1
内存相对便宜,并且方法开销仅为每个类(而非每个实例)。PHP还可以处理同一进程中的多个请求,例如通过mod_php等方式,因此初始加载时间(无论多么微不足道)可以完全忽略......无论如何,首先进行基准测试(在实际条件下)以查看系统的“慢”部分是什么。(顺便说一句,假设有600个方法,每个方法“浪费”1kb。整整600kb都被“浪费”了。从事物的角度来看,这并不是很多......特别是考虑到PHP中每个变量已经“浪费”的数量有多少;-) - user166390
5
你不必担心解析时间。这可以很容易地通过使用APC(或其他opcode缓存)来消除。 - linepogl
@user1476490 “数千用户”加载脚本意味着1000个“并发”请求(即第一个请求仍在运行时,最后一个请求开始)。这是不太可能的情况。高负载仍然不意味着同时服务于10-20k个用户。 - lanzz
伙计,别这样做。如果你有那么多的并发请求/秒,你已经有一个处理流量的服务器集群,拥有memcached、APC、可能还有hiphop,以及各种其他的优化。这些愚蠢的微小优化应该是你最后需要担心的事情。 - Karoly Horvath
显示剩余3条评论
6个回答

5
简单的测试显示,实例占用的内存相同,在类中方法的数量不受影响:
没有方法的类:
class Test1 { }

具有20个方法的类:

class Test2 {

    function test1() { return true; }
    function test2() { return true; }
    function test3() { return true; }
    function test4() { return true; }
    function test5() { return true; }
    function test6() { return true; }
    function test7() { return true; }
    function test8() { return true; }
    function test9() { return true; }
    function test10() { return true; }
    function test11() { return true; }
    function test12() { return true; }
    function test13() { return true; }
    function test14() { return true; }
    function test15() { return true; }
    function test16() { return true; }
    function test17() { return true; }
    function test18() { return true; }
    function test19() { return true; }
    function test20() { return true; }

}

测试循环,两个测试均相同:

$test = array();
$base = memory_get_usage();
for ($i = 0; $i < 10000; $i++) {
    $test[] = new ClassToBeTested();
}
$used = memory_get_usage() - $base;
print("used: $used\n");

Test1类的结果(没有方法):

used: 3157408

Test2类的结果(20个方法):

used: 3157408

我已经在两个不同的脚本中运行了它,因为在单个脚本中运行这两个测试显然会暴露一些PHP内部分配问题,而第二个测试消耗的内存比第一个少,无论哪个先执行。

虽然你肯定需要更多的内存来定义实际的类,但显然这个成本仅针对每个类而非每个实例产生。你不必担心内存使用情况。


很遗憾,我无法在此处粘贴大量代码,但以下是我的做法。我创建了test1.php文件,其中包含一个类;然后创建了test2.php文件,其中包含一个类和一个方法;最后创建了test3.php文件,其中包含一个类、20个私有属性和40个公共的get/set方法(包括所有返回值等)。并在最后输出总使用的RAM,因此这是我得到的结果:test1.php - 653.97 Kbytes test2.php - 654.73 Kbytes test3.php - 718.16 Kbytes这只是一个大致的估算,基本上每个方法/变量(不计算其所持有的数据)占用约1kb。 - user1476490

5

但是考虑到一个类将拥有20个属性

拥有这么多属性通常表明信息被放错了位置。检查一下是否可以将其中的一些分组成自己的类。

除此之外,我还需要添加40个方法。

不完全正确。除非这些类是愚蠢的数据结构,否则您不希望在它们上面使用任何Getter和Setter,因为它们会破坏封装性。将方法放在公共API中,通过它们告诉对象要做什么。

我的担忧是这会使脚本放慢,需要更多的内存(记住我将有几个类似的类)。

这不是问题。

另一个解决方案可能是使用魔术函数__set,__get,但我不想这样做,因为开发IDE上的代码完成不会建议对我至关重要的属性。

现代IDE可以在魔术方法上自动完成。

然而,如果你已经关注微观层面的性能,那么你不希望使用魔术方法,因为那些肯定更慢。

除此之外,魔术方法并不是替代getter和setter的方法,而是在调用无法访问的属性或方法时触发的错误处理程序。 此外,魔术方法并不明显,会导致API难以阅读。

1

0
你可以尝试这个:
特质 get_set {
public function set($what, $value)
{
    $this->{$what} = $value;
}

public function get($what)
{
    return $this->{$what};
}

}

它将适用于公共和受保护的变量。您可以添加if(!isset($ this-&gt; {$ what})error()


0
请注意,我的代码考虑到属性名已经声明为小写...
  <?php

    class Modelo {
        var $attr1 = "default";
        var $attr2 = 0;


        public function __call($name, $arguments)
        {
            if (method_exists($this, ($method = $name))){
                return $this->$method();
            }
            else{       
                $attribute = split("get",$name);
                if(count($attribute)==2){
                    $attribute = strtolower($attribute[1]);
                    if(isset($this->$attribute)){
                        return ($this->$attribute);
                    }
                }else{
                    $attribute = split("set",$name);
                    if(count($attribute)==2){
                        $attribute = strtolower($attribute[1]);
                        if(isset($this->$attribute) && count($arguments)==1){
                            $this->$attribute=$arguments[0];
                        }else{
                            die("$name number of arguments error: ".join($arguments,","));
                        }
                    }else{
                        die("$name doesn't exist!");
                    }               
                }           
            }
        }


    }

    echo "<pre>";
    $m = new Modelo();
    print_r(
        array(
            "objetct"=>$m
            ,"getAttr1"=>$m->getAttr1()
            ,"getAttr2"=>$m->getAttr2()
        )
    );
    echo "setAttr1\n";
    $m->setAttr1("by set method");
    print_r(
        array(
            "objetct"=>$m
            ,"getAttr1"=>$m->getAttr1()
            ,"getAttr2"=>$m->getAttr2()
        )
    );

    ?>

谢谢,但在这种情况下,例如在Eclipse或NetBeans中,代码自动完成将如何工作? - user1476490

0

如前所述,你的类应该有这么多属性是相当奇怪的。然而,有时候(虽然很少)会发生这种情况。但通常,这些属性应该有一种联系:所以你可以将它们存储在哈希映射中而不是属性中。然后你只需要一个方法作为getter。

现在,这肯定会消耗更多的资源,没错。至于自动完成,使用常量:你只需要输入类似于:

 $my_class->getFromHashMap($parameter)

当输入参数时,您将使用类中存储的常量:在这里,自动完成应该能够帮助您。


是的,我目前将所有属性保存在关联数组中,但对于最终用户(开发人员)如何使用它们并不十分清晰,因此我必须使用代码完成和文档注释,另外我想通过验证用户设置变量时的数据来改进代码。哈希映射是一个好的解决方案,但这会导致其他可能的问题(开发人员可能会错误地输入参数名称,如果我想重命名参数,则无法使用重构等)。 - user1476490
除了数据验证之外,使用常量作为哈希映射的键应该可以解决您所描述问题的大部分(甚至可以在设置函数中防止使用未在常量中定义的键)。 - Raveline

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接