gpt4 book ai didi

php - 为什么单功能访问器似乎被认为是坏习惯?

转载 作者:行者123 更新时间:2023-12-04 14:00:16 25 4
gpt4 key购买 nike

我经常看到(即在Slim框架内)不赞成使用单个函数访问器样式(如下面的[1]中所示),而推荐使用经典的Java-ish 2函数访问器(get/set)(如[2]中所示)下面)
就我个人而言,我宁愿更少的代码行(在[1]中)和更少的键入(获取/设置),而不是像在[3]中那样链接setter调用(我认为这很糟糕)。

我想念什么吗?

 class Test {
protected $body;

// [1] single function accessor
public function body($body = null)
{
if (!is_null($body))
$this->body=$body;
return $this->body;
}

// [2] dual function accessors
public function getBody()
{
return $this->body;
}

// [2] dual function accessors
public function setBody($body)
{
$this->body=$body;

//[3] ok, you could return $this for chaining
}
}

最佳答案

单功能访问器是否有不良习惯?

他们不是一个好主意。原因很简单:

  • 他们有多重职责(设置和获取数据)。好的职能只有一项责任,并且要做好。
  • 他们掩盖了意图。您无法查看方法调用并无法理解将要发生的情况。

    您认为body()方法做什么?好吧,body是一个名词。并有一种方法(应该是动词)作为名词是令人困惑的。

    但是,如果该方法是age(),该怎么办?年龄既是动词又是名词。因此,当您看到$obj->age()时,是否要告诉该对象您的年龄?还是您要告诉对象自己变老?
    $obj->getAge()非常清楚您要执行的操作。 $obj->getBody()同样清晰。
  • 它增加了方法的复杂性。

    您的总体复杂度将保持不变(因为存在相同的逻辑),但是它将转移到更少的方法上。这意味着您将拥有一个更复杂的方法,而不是拥有2个简单的方法。

  • 是的,我会避免的。

    但是,让我们退后一分钟。与其问“单功能访问器”是否是个坏习惯,不如问一个关于访问器的一般问题。

    属性(property)访问者是否有不良习惯?

    我的回答是:是的。

    取决于对象的角色:

    它所依赖的是对象的角色以及特定属性的角色。这是逐案的基础。

    有大量不同类型的对象(域对象,服务,代理,集合等)。有些是有状态的,有些则不是。

    如果对象不是有状态的,则它没有属性,因此我们可以忽略它。

    对于那些具有状态的对象,为什么它们具有该状态?

    如果是因为它们代表国家,那么国家应该是公共(public)的(不是说属性(property)应该是公共(public)的,而是国家应该暴露于外界)。因此,代表业务实体的领域模型应具有公共(public)状态。
    class User {
    public $name;
    }

    但是,如果对象的作用不是代表状态,而是要对状态做一些事情,那么就不应公开对象。
    class UserMapper {
    protected $pdo;
    public function __construct(Pdo $connection) {
    $this->pdo = $connection;
    }
    public function findUserById($id) {
    ...
    }

    在映射器的情况下, $pdo是附带状态。映射器的工作不是代表数据库连接的数据,而是需要它来工作。

    底线

    最重要的是 从不公开对象未直接表示的状态。

    仅仅因为您拥有一个属性,并不意味着您应该公开它。实际上,很多时候您不应该公开它。这称为 Information Hiding

    取决于状态类型:

    但是有状态对象的类型又如何呢?好吧,事实证明,状态有两种基本类型:
  • 应用程序状态

    考虑配置之类的事情。基本上声明在构建时未定义,但在运行时已知。

    重要的是要注意,此状态在请求过程中不应更改。并且它也应该合理地是相同的请求到请求(除了部署等)。
  • 用户状态

    考虑派生或依赖于用户输入的状态和数据。一个明显的例子是从表单提交的数据。

    但是,一个不太明显的示例是,如果对不同类型的表示形式使用不同的渲染器。因此,如果用户请求JSON响应,则在属性中设置的JSON呈现代码将被视为用户状态。

  • 我的主张:

    应用程序状态的属性访问器不正确。应用程序状态没有理由在运行中更改,因此也没有理由必须传播该状态。

    用户状态的属性访问器可能没问题。但这还不止于此。

    取决于访问者的操作

    以您的示例为例:
    public function setBody($body)
    {
    $this->body=$body;
    }

    您实质上是将 $body设置为公共(public)属性。

    而且 没什么错。但是,为什么需要一种方法?这是怎么回事: public $body;首先?

    有人会说“属性(property)是邪恶的,绝对不能公开!”。这很费力,因为那正是您使用该访问器所做的。

    现在,如果访问器做了一些键入信息(通过类型提示)或其他验证逻辑(最小长度等),那么情况就不一样了...

    还是吗?

    让我举个例子。
    class Person {
    public $name;
    public $age;
    }


    class StrictPerson {
    protected $name;
    protected $age;

    public function setName($name) {
    if (!is_string($name)) throw new BlahException();
    if (strlen($name) < 10) throw new InvalidException();
    $this->name = $name;
    }
    public function getName() {
    return $this->name;
    }
    public function setAge($age) {
    if (!is_int($age)) throw new ....
    if ($age < 0 || $age > 150) throw new ...
    $this->age = $age;
    }
    public function getAge() {
    return $this->age;
    }
    }

    现在,很明显这些属性始终有效。正确的?正确的?正确的?

    好吧,不。如果我创建了一个 child 会发生什么:
    class LoosePerson extends StrictPerson {
    public function setName($name) {
    $this->name = $name;
    }
    public function setAge($age) {
    $this->age = $age;
    }
    }

    突然之间,我们所有的验证都消失了。现在您可以说这是有意的,这是程序员的问题。或者,您可以简单地将属性更改为 private,以使它们始终有效。正确的?正确的?正确的?

    好吧,不。如果执行此操作会发生什么:
    $a = new StrictPerson;
    $r = new ReflectionProperty($a, 'name');
    $r->setAccessible(true);
    $r->setValue($a, 'Bob');

    我只是在应该始终验证的对象上设置了无效值。

    底线

    仅当 始终使用访问器时,才将访问器用作验证器。而且,您使用的每个工具都会始终使用它们。像mysqli和PDO以及Doctrine和PHPUnit之类的东西会直接设置属性而不是调用setter,这可能会引起很多问题。

    相反,您可以使用外部验证器:
    class PersonValidator {

    public function validate(Person $person) {
    if (!is_string($person->name)) {
    throw new Blah...
    }
    if (strlen($person->name) < 10) {
    throw new Blah...
    }
    if (!is_int($age)) throw new ....
    if ($age < 0 || $age > 150) throw new ...
    return true;
    }
    }

    因此,属性(property)存取人是否有不良习惯?

    我认为是的,很多时候他们是个坏习惯。

    现在,在某些情况下应使用它们:
  • 当您必须在接口(interface)中表示该状态时

    在接口(interface)中未指定属性。因此,如果必须表示该对象在接口(interface)中公开状态,则应使用getter和setter。

    强烈敦促您考虑为什么要创建界面。对于简单的数据对象,通常最好依赖于核心对象类型(由于数据对象上没有逻辑,因此多态性无法受益)。
  • 当出于某种原因需要隐藏内部表示形式时。

    一个示例是代表unicode字符串的类。您可能有一个访问器方法来获取原始值。但是,您希望将其用作一种方法,以便可以将内部表示形式转换为具有适当字符集的字符串。

  • 这提出了一个有趣的观点。

    创建访问器时,永远不要创建属性访问器。您应该改为创建状态访问器。不同之处在于,属性访问器必须始终返回或设置属性)。

    另一方面,状态访问器可以根据需要“伪造”它。因此,我们上面有关unicode字符串类的示例可以在内部将字符串表示为代码点的数组。然后,当您访问“字符串”状态时,它将把数组转换为实际的字符串。

    对象应该抽象

    如果要使用访问器,应该是抽象状态。不代表它。

    底线/TLDR

    属性访问器是一个坏习惯。如果要这样做,则将属性设为公共(public)并使用验证器对象。

    状态访问器不是一个坏习惯。它们对于维护有用的抽象非常有用。

    关于php - 为什么单功能访问器似乎被认为是坏习惯?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25502335/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com