【问题标题】:Does __get() and __set() mean leaky encapsulation?__get() 和 __set() 是否意味着泄漏封装?
【发布时间】:2014-06-03 16:18:13
【问题描述】:

在OO编程中使用魔术方法__get()__set()会被看不起,这些会导致封装泄漏出一个类吗?例如:

class User {
    private $username;
    private $password;

    public function __set($name,$value) {
        $this->$name = $value;
    }

    public function __get($name) {
        return $this->$name;
    }
}

这有效地使private/protected 变量public

【问题讨论】:

  • 不!这取决于实施。
  • __get__set 仅在“无法访问的属性”上调用。 password 在技术上“无法访问”,因为它是 private。因此,当您访问password 时,会调用__get()。这样做可以“泄漏”private 变量。问题是......为什么你会像这样首先将这些方法添加到你的类中?如果您需要 __get()__set() 方法,一种常见的方法是添加 private $data = array() 并让它们访问该数组。 $this->data[$name] = $value;php.net/manual/en/language.oop5.overloading.php#example-228
  • __get、__set、__call 和 __invoke 基本上是 PHP 实现 operator overloading 的方式。虽然这并不完全正确(因为可访问的属性不会使运算符重载),但我觉得这是一个很好的比较,因为如果您正确使用 __get 和 __set ,那么您将在与使用运算符重载的相同情况下使用它们。基本上,当您的对象以这种方式运行但又不会破坏封装时,您可以使用它。

标签: php oop


【解决方案1】:

您的代码:

class User {
    private $username;
    private $password;

    public function __set($name,$value) {
        $this->$name = $value;
    }

    public function __get($name) {
        return $this->$name;
    }
}

在这种情况下是完全没有必要的。

封装does not mean“一堆getter和setter”。您可以将其重构为:

class User {
    public $username;
    public $password;
}

就封装而言,它们是等价的。

一般__get__set 有一些用途,但如果你可以不用,你应该(特别是考虑到它们是"considerably slower" 而不是普通的方法定义)。

【讨论】:

  • 我知道它们是等价的,那么考虑到封装是 OOP 的基本概念,这对 OOP 来说是不是一个坏主意?
  • @Pitchinnate 封装不是由 getter 和 setter 组成的(正如我所说)。 __get__set 本身并不坏,如果你在不需要的时候使用它们就很糟糕。
【解决方案2】:

如果你完全那样使用​​它,那么是的。行为将完全相同,您应该只使用公共变量,因为这在 PHP 中更快。

setter 有用的是在实际分配值之前检查值:

public function __set($name, $value)
{
    switch($name)
    {
        case 'username':
          if(strlen($value) < 6 || strlen($value) > 30)
            throw new Exception();

          ...
          break;

        case 'password':
          //some security checks
          break;

        default: throw Exception('no property'.$name);
    }

    $this->$name = $value;
}

【讨论】:

  • 请详细说明。您的答案是正确的,但这里没有足够的信息来帮助提出问题的人和其他可能找到您答案的人。
  • @Brad 现在好点了吗?
【解决方案3】:

这个问题没有严格的“是/否”答案。相反,有一个解释:

从纯粹的面向对象的角度来看,是的,这是一个泄漏的封装。为什么?考虑一个类有很多属性的情况,每个属性都有一个getter和一个setter。现在,如果这个类被用在一个大而复杂的应用程序中,在很多地方和很多方面,那么控制对象的状态(属性值)变得很困难,因为它可以在很多方面以多种方式修改/改变位置,通过访问者(getter 和 setter)。那么,解决方案是什么?嗯,一般来说,更好的设计是通过构造函数来构造一个对象。例如,可以将 User 类重新设计为:

class User {
    private username;
    private password;

    // constructor
    public User(name, password) {
        this->username = name;
        this->password = password;
    }

    // other methods
}

在这个新设计中,只有一种方法可以使用构造函数来创建用户类型的对象。这保证了对象是以一种已知的方式,以一种受控的方式创建的,因此它的行为在任何时候都是已知的,是可预测的。

考虑到上面的论点,不是getter使封装泄漏,而是setter,因为它们是修饰符;可以修改对象的状态 1) 在构造函数之外 2) 任何时候,除了构造时间。

话虽如此,在某些情况下,必须为类提供 getter 和 setter。例如,某些对象关系框架需要这样做,以便它们能够修改对象。

【讨论】:

    猜你喜欢
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    • 2010-12-01
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    相关资源
    最近更新 更多