6

只是一个简单的问题。我见过很多代码实现如下的情况:

class User
{
    private $id;
    private $firstname;
    private $lastname;

    public function __construct() { some code }

    public function getId() { return $this->id; }

    public function getFirstname() { return $this->firstname; }

    public function setFirstname($value) { $this->firstname = $value; }
}

// And properties are accessed like:
$user->getFirstname();
$user->getId();

那么使用私有属性和拥有公共 getter 的原因是什么,而不是公开属性并直接访问它们,例如:

$user->firstname;

PS:我用第二种方法可以吗?

编辑

好像我在问这个问题之前没有好好研究(我猜我使用了错误的键来搜索这个主题)。这是(几乎)相同问题的另一个很好的答案:https ://stackoverflow.com/a/1568230/1075534

基本上,就我而言,使用 getter 和 setter 的一个很好的理由是避免更改直接访问该属性的 250 个类。例如,假设我没有使用 getter:

class User
{
    public $firstname = 'abc';
    public $lastname  = 'cde';
}

$user = new User(); 

echo $user->firstname . ' ' . $user->lastname;

现在,假设我想改变我的应用程序的行为,我决定将名称打印为大写。在这种情况下,我将搜索每个实现(在这种情况下为 250 :) 并在我调用属性的任何地方将输出大写。但是,如果我使用 getter,那么我只会更改 getter 方法:

class User
{
    private $firstname = 'abc';
    private $lastname  = 'def';

    public getFirstname()
    {
        return ucfirst(strtolower($this->firstname));
    }

    public getLastname()
    {
        return  ucfirst(strtolower($this->lastname));
    }
} 

另外,请记住,getter 可能不仅会从单个属性中收集信息。想象一下:

class User
{
    private $firstname = 'abc';
    private $lastname  = 'def';

    public function getName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}

对于那些对此仍有疑问的人,我建议他们阅读 Gordon 提供的材料,尤其是我链接的答案。

4

3 回答 3

10

使用访问器满足统一访问原则

引用维基百科:

统一访问原则是由 Bertrand Meyer 提出的。它指出“一个模块提供的所有服务都应该通过统一的表示法提供,这并不表明它们是通过存储还是通过计算实现的。” 这个原则通常适用于面向对象的编程语言。以更简单的形式,它指出使用属性、预计算属性或方法/查询之间应该没有区别。

福勒在他的博客中解释说

它实际上意味着该人的客户既不应该知道也不关心年龄是否被存储或计算。这使人员对象可以灵活地在两者之间轻松切换,并消除客户通常不必要的担忧。它是封装的重要部分——或者至少是封装的数据隐藏方面。

为了说明这一点,想象一个这样的类:

class Now
{
    public $timestamp;

    public function getTomorrow()
    {
        return strtotime('+1 day', $this->timestamp);
    }

 // … more code

当您这样做时,您会迫使使用该类的开发人员了解实现细节。要获得现在的时间戳,开发人员必须这样做

echo $now->timestamp;

而要获得明天的计算时间戳,开发人员必须这样做

echo $now->getTomorrow();

但是开发者不需要关心,所以如果你想遵循 UAP,你将提供一个 Accessor 来获取时间戳。并通过方法汇集所有访问权限。

这样做还有一个额外的好处,那就是更稳定的 API。想象一下,您需要在项目后期更改实现细节并将时间戳转换为 DateTime 对象。您的课程现在看起来像这样:

class Now
{
    public $dateTime; // <-- no longer just a timestamp

    public function getTomorrow()
    {
        return strtotime('+1 day', $this->dateTime->getTimestamp());
    }

 // … more code

现在,以前使用过的任何开发人员$now->timestamp都必须更改其使用代码以适应该更改。而当您从一开始就使用 Getter 时,这根本不是问题,因为 Getter 会确保它返回时间戳。为了进一步证明这一点,请注意开发人员无需更改任何内容即可使用getTomorrow()。尽管我们在内部更改了细节,但公共 API 的行为仍然相同。

请注意,UAP 只是一个指南。现代 IDE 让为您生成 Accessors 和 Mutators 变得很容易,因此很容易遵循。但这不是绝对的真理。如果您可以合理地证明不遵循它,那么请不要遵循它并使用公共属性。但这应该是一个明智的决定。

但是,一般来说,您无论如何都希望避免 Getter(和 Setter),而只是告诉您的对象要做什么。这将提供更精简的 API。如果您的 API 有很多很多的 Getter,那么您很可能会将真正应该在对象内部的代码分散到消费者中。

于 2013-06-21T09:53:34.873 回答
2

当一个变量被声明为 public 时,它可以从任何类中访问,这使您作为程序员更容易使用;从而解释您希望使用您描述的第二种方法。但是,出于安全原因,某些变量需要声明为私有(有时也被视为一种好的做法)。我们将它们声明为私有的事实使它们只能在它们自己的类中访问。但是,如果您需要在另一个类的函数中使用它们,那么您需要遵循您描述的第一种方法。

于 2013-06-21T09:55:54.997 回答
2

使用 getter 使其只读。

(这是唯一的概念差异。一种语言可以很容易地允许根据属性和方法定义接口。命名约定并不是真正的基础。)

于 2013-06-21T10:27:03.840 回答