OOP - 使用私人财产和公共吸气剂

Sav*_*ova 6 php oop

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

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();
Run Code Online (Sandbox Code Playgroud)

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

$user->firstname;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

现在,想象一下,我想改变我的应用程序的行为,我决定将名称打印为大写.在这种情况下,我会搜索每个实现(在这种情况下为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));
    }
} 
Run Code Online (Sandbox Code Playgroud)

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

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

    public function getName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}
Run Code Online (Sandbox Code Playgroud)

对于那些仍然对此事有疑问的人,我建议他们阅读戈登提供的材料,特别是我已经联系到的答案.

Gor*_*don 10

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

引用维基百科:

统一访问原则由Bertrand Meyer提出.它声明"模块提供的所有服务都应该通过统一的符号提供,不会背叛它们是通过存储还是通过计算实现的." 该原理通常适用于面向对象的编程语言.在更简单的形式中,它声明使用属性,预计算属性或方法/查询之间应该没有区别.

福勒解释说,在他的博客中

它实际上意味着该人的客户既不知道也不关心年龄是否被存储或计算.这使得人物对象可以灵活地在两者之间进行切换,并且从客户端移除通常不必要的问题.它是封装的一个重要部分 - 或者至少是封装的数据隐藏方面.

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

class Now
{
    public $timestamp;

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

 // … more code
Run Code Online (Sandbox Code Playgroud)

当您这样做时,您正在强制开发人员使用该类来了解实现细节.要获得现在的时间戳,开发人员必须这样做

echo $now->timestamp;
Run Code Online (Sandbox Code Playgroud)

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

echo $now->getTomorrow();
Run Code Online (Sandbox Code Playgroud)

但开发人员不需要关心,因此如果您想要遵循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
Run Code Online (Sandbox Code Playgroud)

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

请注意,UAP只是一个指导原则.现代IDE使您可以轻松地为您生成Accessors和Mutators,因此很容易理解.但这不是绝对的事实.如果您可以合理地证明不遵守它,那么请不要遵循它并使用公共属性.但它应该是一个明智的决定.

但是,一般来说,你想要避免使用Getters(和Setters),只需告诉你的对象该做什么.这将使API变得更加纤薄.如果您的API有很多很多Getters,那么您很可能会将对象内部的代码分散到消费者中.