Php 不使用setter和getter真的那么错误吗?

Php 不使用setter和getter真的那么错误吗?,php,oop,Php,Oop,我对PHP有点陌生。出于某种原因,在其他类型的编程语言(如JAVA)中,我对每个变量都使用setter和getter没有问题,但是当我使用PHP编程时,可能因为它非常灵活,感觉有点浪费时间。在大多数情况下,只将类属性设置为公共属性并像那样操作它们会让人感觉更简单。问题是,当我这样做的时候,我觉得我做错了什么,违背了OO原则 不使用setter和getter真的那么错误吗?为什么?大多数时候你们是怎么做的?getter或setter的要点是,您仍然可以在一个地方为字段的修改添加逻辑,而不是在您想要

我对PHP有点陌生。出于某种原因,在其他类型的编程语言(如JAVA)中,我对每个变量都使用setter和getter没有问题,但是当我使用PHP编程时,可能因为它非常灵活,感觉有点浪费时间。在大多数情况下,只将类属性设置为公共属性并像那样操作它们会让人感觉更简单。问题是,当我这样做的时候,我觉得我做错了什么,违背了OO原则


不使用setter和getter真的那么错误吗?为什么?大多数时候你们是怎么做的?

getter或setter的要点是,您仍然可以在一个地方为字段的修改添加逻辑,而不是在您想要修改或检索字段的每个地方。你也可以在类级别上控制该字段会发生什么。

< P>你是否考虑使用魔法函数?使用它们,您可以轻松地将所有getter/setter函数合并到2个函数中

不使用属性访问器的主要问题是,如果您发现以后需要将字段更改为属性(例如,使其成为子类中的计算属性),您将破坏API的客户端。对于已出版的图书馆来说,这是不可接受的;对于一个内部的,只是相当多的工作修复的东西

对于私有代码或小型应用程序,只需使用它是可行的。IDE(或文本编辑器)将允许您生成访问器样板,并使用代码折叠将其隐藏。这使得机械地使用getter和setter变得相当容易


注意,一些编程语言具有合成默认字段+getter+setter的功能——Ruby通过元编程实现,C#具有自动实现的属性。Python完全回避了这个问题,它允许您覆盖属性访问,允许您将属性封装在需要它的子类中,而不必预先处理它。(这是我最喜欢的方法。)

如果你在脚本中多次访问这些变量,并且如果你经常更新你的类,你应该使用setter和getter,因为如果你不这样做,当你改进你的类时,你必须更新所有使用这个变量的文件


第二个主要原因是您不应该直接访问变量,因为类结构可能会发生变化,并且这些数据可能会以不同的方式保存。当您从类中获取数据时,您不应该关心这些数据是如何生成的。类必须关心这些数据的处理,所以您只应该关心您将要做什么获取。

我可能不会在这个问题上获得太多的赞成票,但就个人而言,获得者甚至更多,因此,制定者对我来说就像是一种代码气味。设计应该是行为驱动的,而不是数据驱动的。当然,这只是一种观点。如果您有一个依赖于另一个对象的特定数据字段的对象,这是非常紧密的耦合。相反,它应该取决于对象的行为,而对象远没有其数据那么脆弱


但是,是的,像getter和setter这样的属性正是因为这个原因,才从对字段的依赖性上向前迈进了一步。它不那么脆弱,并且放松了对象之间的耦合。

有一种方法可以在不实际使用get/set函数类的情况下模拟get/set,因此您的代码保持整洁:

$person->name = 'bob';
echo $person->name;
看看我的密码

通常,在使用此类时,您会声明所有受保护(或私有)的属性。如果您想在属性上添加行为,比如在“name”属性上添加strtolower()+ucfirst(),那么您需要做的就是在类中声明一个受保护的set_name()函数,该行为应该会被自动拾取。使用get_name()也可以实现这一点

附言。 另一件很酷的事情是,您可以创建不存在的字段,例如

echo $person->full_name;

如果没有这样的字段(只要有一个get_full_name()函数)。

如果我们在这里严格谈论的是PHP,而不是C#、Java等(编译器将优化这些内容),我发现getter和setter是一种资源浪费,您只需要代理私有字段的值,而不做其他事情

在我的设置中,我创建了两个蹩脚的类,一个类有五个私有字段,由五个getter/setter对封装,代理该字段(有趣的是,它看起来几乎完全像java代码),另一个类有五个公共字段,并在创建实例后调用memory\u get\u usage()。带有getter/setter的脚本使用了59708字节的内存,带有公共字段的脚本使用了49244字节

在任何重要大小的类库(如网站框架)的上下文中,这些无用的getter和setter都可能构成一个巨大的内存黑洞。我一直在用PHP为我的雇主开发一个框架(他们的选择,不是我的。如果我有选择的话,我不会使用它,但我已经说过,PHP并没有对我们施加任何不可逾越的限制),当我重构类库以使用公共字段而不是getter/setter时,最终,整个shebang在每个请求中使用的内存至少减少了25%

_uuuget()、uuu set()和uuuu call()的“magic”方法在处理接口更改时非常有用。当需要将字段迁移到getter/setter(或将getter/setter迁移到字段)时,它们可以使流程对任何依赖代码透明。使用解释语言时,即使EclipsePDT或Netbeans提供了相当好的代码敏感度支持,也很难找到字段或方法的所有用法,因此神奇的方法对于确保旧接口仍然委托给新功能非常有用

假设我们有一个对象是使用字段而不是getter/setter开发的,我们想将一个名为“field”的字段重命名为“fieldWithBetterName”,因为“field”不合适,或者不再描述其用法
echo $person->full_name;
class Test extends Object {
    public $field;
    public $field2;
}
class Test extends Object {
    public $fieldWithBetterName = "LA DI DA";
    private $_field2;

    public function getField2() {
        if ($this->_field2 == null) {
            $this->_field2 = CrapDbLayer::getSomething($this->fieldWithBetterName);
        }
        return $this->_field2;
    }

    public function __get($name) {
        if ($name == 'field')) {
            Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace());
            return $this->fieldWithBetterName;
        }
        elseif ($name == 'field2') {
            Logger::log("use of deprecated property... blah blah blah\n".DebugUtils::printBacktrace());
            return $this->getField2();
        }
        else return parent::__get($name);
    }
}
$t = new Test;
echo $t->field;
echo $t->field2;
class Test extends Object {
    public $field2;

    public function __call($name, $args) {
        if (strpos($name, 'get')===0) {
            $field = lcfirst($name); // cheating, i know. php 5.3 or greater. not hard to do without it though.
            return $this->$field;
        }
        parent::__call($name, $args);
    }
}