为什么单函数访问器似乎被认为是坏习惯?

2024-03-26

我经常看到(即在 Slim 框架内)单一函数访问器风格(如下面的[1])已弃用,取而代之的是经典的 Java-ish 2 函数访问器(get/set)(如下面的[2])。 就我个人而言,我更喜欢更少的代码行(在 [1] 中)和更少的输入(get/set),而不是能够像 [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(),你是在告诉物体给你它的年龄吗?或者你是在告诉物体自己老化?

    Whereas $obj->getAge()非常清楚你想要做什么。和$obj->getBody()同样清楚。

  • 它增加了方法的复杂性。

    您的整体复杂性将保持不变(因为存在相同的逻辑),但它会转向更少的方法。这意味着您将拥有一种更复杂的方法,而不是两种极其简单的方法。

所以是的,我会避免它。

但让我们退后一步。与其问“单函数访问器”是否是一个坏习惯,不如让我们问关于一般访问器的同样问题......

属性访问器是一个坏习惯吗?

我的回答是:是的。

取决于对象的角色:

它取决于的是对象的作用,以及具体属性的作用。这是根据具体情况而定的。

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

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

对于那些有状态的对象来说,它们为什么有那个状态呢?

如果是因为他们代表国家,那么国家就应该是公开的(不是说财产应该公开,而是国家应该向外界公开)。因此,代表业务实体的领域模型应该具有公共状态。

class User {
    public $name;
}

但如果对象的作用不是代表状态,而是用它做某事,那么它就不应该被暴露。

class UserMapper {
    protected $pdo;
    public function __construct(Pdo $connection) {
        $this->pdo = $connection;
    }
    public function findUserById($id) {
        ...
}

在映射器的情况下,$pdo是偶然状态。映射器的工作不是表示数据库连接的数据,但它需要它才能工作。

底线

底线是never暴露对象不直接表示的状态。

仅仅因为您拥有财产,并不意味着您应该公开它。事实上,很多时候你不应该暴露它。这被称为信息隐藏 http://en.wikipedia.org/wiki/Information_hiding.

取决于状态类型:

但是有状态的对象类型又如何呢?事实证明,状态有两种基本类型:

  1. 应用状态

    想想配置之类的事情。基本上状态在构建时未定义,但在运行时已知。

    请务必注意,此状态不应在请求过程中发生更改。而且它也应该是相当相同的请求到请求(除了部署等)。

  2. 用户状态

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

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

我的主张:

属性访问器应用状态很糟糕。应用程序状态没有理由在运行中更改,因此您没有理由必须传播状态。

属性访问器用户状态 may没事。但还有更多的事情要做。

取决于访问器的作用

在你的例子中:

public function setBody($body)
{
    $this->body=$body;
}

你本质上是在做$body公共财产。

还有nothing错了。但为什么需要方法呢?有什么问题:public $body;首先?

有人会说“财产是邪,千万不能公!”。这完全是废话,因为这正是您对该访问器所做的事情。

现在,如果访问器执行了一些输入信息(通过类型提示)或其他验证逻辑(最小长度等),那么这是一个不同的故事......

或者是吗?

让我举个例子。

class Person {
    public $name;
    public $age;
}

vs

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;
    }
}

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

嗯,不。如果我创建一个孩子会发生什么:

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');

我刚刚在一个应该始终验证的对象上设置了一个无效值。

底线

仅当您满足以下条件时,才可以使用访问器作为验证器:always使用它们。你使用的每一个工具总是会用到它们。像 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;
    }
}

那么,属性访问器是一个坏习惯吗?

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

现在,在某些情况下您应该使用它们:

  • 当你must在界面中表示该状态

    属性在接口中是不可指定的。所以如果你must表示对象在接口中公开状态,那么您应该使用 getter 和 setter。

    I'd strongly敦促您考虑为什么要创建该界面。对于简单的数据对象,通常最好依赖核心对象类型(因为由于数据对象上没有逻辑,多态性无法受益)。

  • 当您出于某种原因需要隐藏内部表示时。

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

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

当您创建访问器时,绝对不应该创建属性访问器。您应该创建一个状态访问器。不同之处在于属性访问器必须始终返回属性或设置它)。

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

对象应该抽象

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

底线 / TLDR

Property访问器是一个坏习惯。如果您打算这样做,请将属性公开并使用验证器对象。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么单函数访问器似乎被认为是坏习惯? 的相关文章

  • 严格标准:资源 ID#73 用作偏移量,转换为整数

    我使用这个 PHP 函数获取 MySql 结果 function fetcharray query id if query id query id this gt query res if query id this gt q array
  • 自定义 php 论坛 - 显示新的/未读的帖子

    我自己使用 php 编写了一个自定义论坛脚本 我决定不使用 phpbb 和其他工具 因为我希望我所做的事情具有 100 的灵活性 不过我遇到了一个问题 如何向用户显示帖子是否是新的 未读的 我想到了两种解决方案 1 饼干 2 数据库 我不想
  • 将 XML 数据提取到 php [重复]

    这个问题在这里已经有答案了 我正在尝试从 XML 文件中提取数据 http freegeoip net xml google com http freegeoip net xml google com 您可以看到该文件的内容类似于
  • 在 PHP 中验证约 400MB 的大型 XML 文件

    我有一个很大的 XML 文件 大约 400MB 在开始处理之前我需要确保它的格式正确 我尝试的第一件事是类似于下面的内容 这很棒 因为我可以找出 XML 是否格式不正确以及 XML 的哪些部分 不好 doc simplexml load s
  • 是否可以共享 Symfony2 安装(一台服务器上的多个网站 [域])

    我想在一个根 服务器 下托管多个基于 Symfony2 的网站 是否可以共享 symfony 自己的文件 供应商等 有人知道教程吗 管理起来困难吗 AFAIK Symfony 安装大约有 600MB 我不想以冗余方式保存它 我看起来确实有可
  • 如何使用图像生成 SVG 二维码?

    我有一个用于生成 jpeg 和 png 格式的 qrcode 的代码 但也想在 PHP 中生成带有背景图像的 svg qrcode 我正在使用 qrlib php 生成 jpeg 和 png 格式 QRlib php 也支持 SVG 不是吗
  • 上传非常大的文件(>5GB)

    我需要你的帮助 我想用 HTML JQuery 和 PHP 创建一个上传脚本 是否可以编写一个可以上传非常大的文件 gt 5 GB 的脚本 我已经尝试使用 FileReader FormData 和 Blobs 但即使使用这些 我也无法上传
  • 如何在Wamp服务器中启用SSL?

    我尝试在网上搜索它 但我很困惑 我没有得到任何澄清 逐步教程 http blog facilelogin com 2008 07 enabling ssl on wamp html 从链接复制 在 WAMP 上启用 SSL 本分步指南介绍了
  • 在 while 循环之外使用变量(作用域)

    关于 PHP 范围的小问题 我似乎无法在 while 循环之外调用变量 report 我尝试过各种事情 包括return 这不起作用 这里唯一起作用的两个函数是如果我echo变量 report在循环内 或者如果我print它 我不想这样做
  • 数组和关联数组合并

    如何实现第三个数组 合并Array1和Array2 在 PHP 中做到这一点的最佳方法是什么 非常感谢 Array2 具有类似索引 键 即 Array1 中 id 的关联值 Array1 Array 0 gt Array id gt 56
  • 使用 Jquery Easyui 将数据网格导出到 Excel

    我是 json 新手 我使用 php 从 mysql 表生成了 jason 数据 并希望将生成的 json 导出为 xls 格式 考试导出 php
  • 需要一个正则表达式将 css 类添加到第一个和最后一个列表项

    更新 谢谢大家的意见 一些附加信息 它实际上只是我正在使用的一小部分标记 20 行 目的是利用正则表达式来完成工作 我还能够修改脚本 电子商务脚本 以在构建导航时插入类 我想限制我所采用的黑客数量 以便在更新到软件的最新版本时让事情变得更容
  • 从本地 html/javascript 网站插入 mySQL 数据库

    我正在尝试做什么 我的程序的目的是插入数据local HTML JS网站变成online 非本地 mySQL数据库 到目前为止我尝试过的 我试图用来实现此目的的原始方法是让我的本地网站使用 javascript 通过在线发布数据PHP文件
  • PHP date_sun_info 错误时间

    我正在尝试使用 PHPdate sun info函数获取全天太阳某些位置的时间信息 目前我正在使用类似于中的代码文档 http php net manual en function date sun info php sun info da
  • 我可以在 Laravel 5.2 中创建一个继承自 User 的新类吗?

    我对 Laravel 还很陌生 使用的是迄今为止的最新版本 5 2 因此我遇到了以下困境 我知道 Laravel 附带了一个User开箱即用的类 但我想开发一个系统 在其中我可以有另外两种类型的用户 称为Researcher and Adm
  • CMS:将自定义页面存储为文件或 MySQL 数据库中?

    我正在 PHP 中创建一个自定义 CMS 从头开始编写 并且想知道是否应该将用户创建的页面存储为文件或存储在 MySQL 数据库中 内容全部是 HTML 代码 至少目前是这样 我无法决定该做什么 因为用 php 编写文件似乎存在安全风险 并
  • 向 yahoo 和 hotmail 用户发送电子邮件?

    我正在使用 php 和 mysql 每次用户在我的网站上注册时 我都会使用 php mail 发送一封电子邮件进行身份验证 最近我发现 很多Yahoo和Hotmail用户还没有激活他们的帐户 假设 1000 个用户中 只有 200 个被激活
  • Woocommerce 中的 Ajaxify 标头购物车项目计数

    我正在为 WordPress 创建一个自定义 woocommerce 集成主题 我在顶部有一个 blob 显示购物车中的商品总数 我想使用 Jquery 更新此 blob 无需重新加载页面 我能够通过获取购物车中的当前数量来增加商品数量bl
  • TCPDF - 来自 mysql 的打印表显示重复的第一行

    我是 TCPDF 的新手 我面临的小问题是所有输出数据都显示同一行 我的意思是第一条记录重复数据库中存在的总数据 行 的次数 这是我的代码 tbl header
  • 如何使用 PHP 从 iframe 获取 url

    如何从下面的链接获取 YouTube 网址 您可以使用 regex 和 preg match 函数 preg match src iframe string match url match 1 UPDATE如果您有使用 php 生成的页面或

随机推荐

  • ValueError:负数不能求小数次方

    当我在终端尝试这个时 gt gt gt 3 66 26 32 0 2 我收到以下错误 Traceback most recent call last File
  • 将 VarBinary RTF blob 转换为 MS SQL 中的文本

    我正在使用 SQL Server 2008 Column Datatype VarBinary RTF 文件被压缩并保存到此 varbinary 列 现在如何使用 SQL 访问和查看 RTF 文件中的数据 它返回这个 弰巎楛 etc 此处尝
  • 带有 JSON 的范围对象

    我正在编写一个 Chrome 扩展程序 我需要将用户在网站上选择的值传递到我的服务器 我正在使用返回范围对象的代码 window getSelection 我正在使用 JSON 将范围对象传递回我的服务器 但它不起作用 我对此很陌生 但我认
  • C# 无法获取某些客户端计算机名称

    我使用下面的代码来获取客户端计算机名称 但是它适用于某些客户端 而某些客户端会出现错误 string IPAdd Request UserHostName IPHostEntry hostEntry Dns GetHostEntry IPA
  • IIS 上的 django,进程意外退出

    我已经阅读了关于该主题的所有文章 但我仍然无法在 IIS 上运行 django 项目 错误消息为 500 Internal Server Error c program files x86 python 3 5 python exe Fas
  • 如何更改 WPF 中绑定选项的默认值?

    在我当前的项目中 我使用了几个文本框控件 其内容由来自数据库的对象填充 该对象使用验证来验证文本的正确插入 当我想显示验证错误 即文本有很多字符 时 我必须向文本属性添加一些绑定选项 如下行所示
  • 删除 json_encode() 中的双引号

    我想删除 json encode 中的双引号 这是我的代码
  • 如何使用 Python 多线程处理 MySQL 连接

    我有一个主要的 Python 脚本 它连接到 MySQL 数据库并从中提取一些记录 根据返回的结果 它会启动与抓取的记录一样多的线程 类实例 每个线程都应该返回数据库并通过将一个状态标志设置为不同的状态 进程已启动 来更新另一个表 为了实现
  • Laravel 将数据透视表附加到具有多个值的表

    背景 我正在创建一个围绕食物过敏的数据库 并且食物和过敏之间存在多对多的关系 还有一个枢轴值称为severity其中有一个数字代表对该食品过敏的严重程度 这个链接表看起来像这样 food id allergy id severity 1 1
  • 指定与 Chef Solo 一起运行哪些食谱

    我正在使用 Chef solo 在本地测试我的食谱 但我希望能够只运行我正在测试的食谱 目前 chef solo 似乎将运行在solo rb 中指定的cookbooks 目录中的所有cookbook 我已经在 json 属性文件中指定了运行
  • 通过 jQuery ajax 调用将值列表传递给 django 视图

    我正在尝试使用 jQuery ajax 调用将数值 ids 列表从一个网页传递到另一个网页 我不知道如何传递和读取列表中的所有值 我可以成功发布和读取 1 个值 但不能成功发布和读取多个值 这是我到目前为止所拥有的 jQuery var p
  • Bootstrap 输入组插件对齐问题

    我的输入组插件与我的输入文本框不对齐 我做错了什么 div class form group div
  • 我无法让 phpseclib 工作

    我想做的事 我想要upload download一个文件通过sftp using php The phpseclib library看起来很有前途 我已经做了什么 我将composer json更改为 require nicolab php
  • 如何在Azure中创建或使用本地文件夹?

    我需要从 SFTP 服务器下载文件 下载的文件存储到本地文件夹 D Data tempData csv 我必须从本地文件读取数据并在我的应用程序中使用以进行其他数据操作 此作业是使用 Azure Web 作业中的 Web 挂钩计划程序创建的
  • 我的“zipLatest”运算符是否已经存在?

    关于我自己写的一个运算符的快速问题 请原谅我可怜的大理石图表 zip aa bb cc dd ee ff gg 11 22 33 44 55 a1 b2 c3 d4 e5 combineLatest aa bb cc dd ee ff gg
  • 使用 f:convertNumber 时设置小数点分隔符

    我想知道如何在 JSF 应用程序上设置默认的小数点分隔符 我有一些
  • NodeJS UDP 多播如何

    我正在尝试将 UDP 多播数据包发送到 230 185 192 108 以便每个订阅的人都会收到 有点卡住了 我相信它的广播正确 但似乎无法从任何客户端获取任何信息 Server var news Borussia Dortmund win
  • 如何将多个参数传递给 Scrapy 蜘蛛(不再支持使用多个蜘蛛运行“scrapy scrapy”时出现错误)?

    我想将多个用户定义的参数传递给我的 scrapy spider 所以我尝试遵循这篇文章 如何在 scrapy 蜘蛛中传递用户定义的参数 https stackoverflow com questions 15611605 how to pa
  • CMTime 和 AVFoundation 中单帧的移动

    我正在尝试使用 AVFoundation 播放视频 我使用以下代码作为将播放前进一帧的按钮 它间歇性地工作 在某些执行中它会做正确的事情并前进一帧 但大多数时候我必须按按钮 3 或 4 次才能前进一帧 这让我认为这是某种精度问题 但我无法弄
  • 为什么单函数访问器似乎被认为是坏习惯?

    我经常看到 即在 Slim 框架内 单一函数访问器风格 如下面的 1 已弃用 取而代之的是经典的 Java ish 2 函数访问器 get set 如下面的 2 就我个人而言 我更喜欢更少的代码行 在 1 中 和更少的输入 get set