27

好的,这真的困扰着我,我开始认为这一切都归结为个人选择,而不是一种更高效或编写更好代码的特定方式:我应该还是不应该在 PHP 项目中使用 getter/setter 方法? 到目前为止,我读过的答案相当矛盾,并不完全适合 PHP,它不是一种编译语言。例如,在 Stack Overflow 上回答这个问题(“为什么使用 getter 和 setter?”)。关于为什么我应该在我的代码中使用它们有很多很好的理由,但是所有子评论都提到了访问器是多么邪恶并且应该被避免,穿插在一些更令人不安的评论之间,这些评论提到应该完全避免它们,因为它“弄乱你的代码”。

我得到的只是相互矛盾的答案,它们都与解释的 PHP 环境无关。有人可以阐明为什么/为什么不应该在 PHP 中使用它们,以及他们做出该决定的理由吗?如果我们可以简单地将属性定义为私有或受保护,这真的很重要,而且无论如何:

封装 getter 和 setter 提供的东西薄得可笑

...引自“sbi”(为什么使用getter和setter?)

就个人而言,我仍然不明白如何:

Class Foo {
    public $bar;

    function setBarType($val) {
       $this->bar = $val;
    }
}

$fee = new Foo();
$fee->setBarType(42);

优于此:

Class Foo {
    public $bar;
}

$fee = new Foo();
$fee->bar = 42;
4

7 回答 7

22

您链接到的博客文章以一个关键句子开头(添加了重点):

代码中的每个 getter 和 setter 都代表封装失败并创建不必要的耦合。

封装是面向对象编程中最重要的思想。它本质上归结为通过将复杂性整齐地包装在类中来隐藏复杂性。在理想的世界中,当你使用一个类时,你不应该知道的内部运作或状态。有些人(比如这个博客作者)会争辩说,拥有 getter 和 setter 已经是关于类内部的太多信息了。在他们看来,一个类应该只具有使我们能够告诉对象做某事的方法,而不管它是如何做的或它处于什么状态。使用 setter 并不是“告诉对象做某事”,而是从外部处理对象的状态。

而不是这样做:

$a = myObject();

// Querying object state, making a decision outside the object, and changing its state
if ($a->getWarbleFizz() < 42) {
    $a->setYourWarbleFizzTo(42);
}

// Gee, I hope I made the right decision...
$a->nowDoSomethingUseful();

您应该编写这样的代码:

$a = myObject(42);
$a->doStuff();

或这个 :

$a = myObject();
$a->doStuff(42);

相关阅读:告诉,不要问

于 2013-08-21T08:33:47.670 回答
15

因为如果值设置方式的实现发生变化(到数据库),您不必更改调用者。另一个例子是您可能需要在设置之前对值进行一些检查。

拥有一个方法可以让您拦截该变量的设置/获取,在您似乎不需要它时执行它会使您的代码对更改更加友好。

属性吸气剂

像 C# 和最新版本的 JavaScript 这样的语言允许您拦截属性读取和写入,因此您可以只使用支持它的语言中的属性。

对象观察者

某些语言允许您拦截所有JavaScript 的 Object.watch的读取/设置,或PHP 的 __get无法访问的属性。这允许您实现 getter 和 setter,但由于它们为每个属性访问创建的开销,您会受到性能影响。这个答案谈到了 getter 和 setter 的其他问题。最佳实践:PHP 魔术方法 __set 和 __get

Getter 和 Setter 都可以,但是...

仅仅制作样板的 getter 和 setter 更好,但几乎和公共属性一样糟糕。如果任何人都可以更改您的对象的状态(特别是具有多个属性),则它不会被很好地封装。 http://cspray.github.io/2012/05/13/stop-calling-them-getters-setters.html

于 2013-08-21T08:01:49.097 回答
8

使用 getter 和 setter 的一大好处是,当您需要进行更改时,您只需修改 getter 和 setter。

我将尝试用一个例子来解释:

protected $date;
public function getDate(){
    return $this->date;
}
public function setDate($date){
    $this->date = $date;
}

想象一下,日期应该总是增加一天是有原因的。您将不得不在您访问班级成员的整个项目中进行搜索。

但是通过使用 getter 和 setter,您可以将代码更改为:

protected $date;
public function getDate(){
    return $this->date;
}
public function setDate($date){
    $date = new DateTime($date);
    $date->modify('+1 day');
    $this->date = $date;
}
于 2013-08-21T08:08:18.297 回答
5

一个 OOP 原则是封装。一个类负责该类中包含的所有变量。

通过设置一个公共变量,你打破了这个原则。

通过创建访问器(设置器),您通过提供在类之外更改该值的机会来间接打破该原则。

getter 的优点是调用方法不需要关心如何检索数据。它只知道它会得到预期的数据,仅此而已。这里遵循封装原则。

setter 的优点是类有机会在应用新值之前检查它。在一个完美的世界中,不需要访问器,因为一个类可以完全管理他的所有变量。在现实世界中,有时您需要设置一个值或从外部获取它。

接近完美世界的最佳方法是将访问者限制为仅需要修改的极少数变量。如果可能,请检查设定值。

关于您的示例,第二种解决方案更好,因为您在 php 堆栈中保存了一个级别。您的访问器是无用的,因为您的变量无论如何都是公共的(因此,没有封装)并且不执行任何附加检查。但如果可能,您应该使用访问器来检查数据。如果应用程序中的其他任何地方都不需要数据,则不要执行任何访问器。

于 2013-08-21T08:09:15.540 回答
4

这个选择由你。PHP 5 其他需要考虑的重要特性是魔术 getter/setter:

http://www.php.net/manual/en/language.oop5.overloading.php#object.set http://www.php.net/manual/en/language.oop5.overloading.php#object.get

这样做的神奇之处在于,您可以执行以下操作并决定在哪里获取/设置变量,而无需事先声明它们:

Class Foo {
    private $data = array();

    function __set($name,$val) {
       $this->$data[$name] = $val;
    }
    function __get($name) {
       if (array_key_exists($name, $this->data)) {
        return $this->data[$name];
    }
    else {
        return null;
    }
}

瞧,这样的事情现在自动发生了:

$fee = new Foo(); $fee->bar = 42;

于 2013-08-21T08:06:42.307 回答
4

如果你不使用像 PHP 这样类型不安全的语言中的 getter 和 setter,那么你将如何确保对象属性的类型是正确的呢?

我可能完全错过了这一点,但如果您需要从对象外部访问属性,我认为它仍然是使用访问器的最佳选择......

此外,正如 Juon Mendes 所提到的:某些语言会在处理属性本身时提供拦截。这也可能会出现在 PHP 中

你甚至可以有一个公共的 setter 和一个受保护的 getter:

class TimePeriod {
    protected $Seconds = 3600;

    public $Hours {
        get { return $this->Seconds / 3600; }
        set { $this->Seconds = $value * 3600; }
    }

    // This property is read only as there is no setter
    public $Minutes {
        get { return $this->Seconds / 60; }
    }

    /* public getter, protected setter */
    public $Milliseconds {
        get { return $this->Seconds * 1000; }
        protected set { $this->Seconds = $value / 1000; }
    }
}
于 2013-08-21T08:06:46.167 回答
2

IMO 使用 getter 和 setter 是一种很好的做法,并且也增加了代码的可读性。除了在设置时检查值,还有另一个例子,考虑你是否调用getMoviesList()。现在这个get方法无论如何都可以实现,它可以从本地数据库中获取电影列表,从在线Web服务中获取等等......所以做出决定的代码可能在这个函数中。无论你从哪里调用它,你都不关心它的位置和它是如何得到它的。你只是得到电影列表

于 2013-08-21T08:08:32.160 回答