为什么单功能访问器似乎被认为是坏习惯? [英] Why single function accessor appears to be considered as bad habit?

查看:76
本文介绍了为什么单功能访问器似乎被认为是坏习惯?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I see quite often (ie within the Slim framework) that single function accessor style (like in [1] below) is deprecated in favor to classical Java-ish 2 functions accessors (get/set) (like in [2] below). Personally I rather prefer less lines of code (in [1]) and less typing (get/set) than being able to chain setter calls like in [3] (which I consider awful).

我想念什么吗?

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

推荐答案

单个函数访问器是否存在不良习惯?

这不是一个好主意.原因很简单:

Are Single Function Accessors A Bad Habit?

They are not a good idea. The reasons are simple:

  • 他们有多重职责(设置和获取数据).好的职能只有一项责任,而且要做好.

  • They have multiple responsibilities (setting and getting data). Good functions have a single responsibility and do it well.

他们掩盖了意图.您无法查看方法调用并无法理解将要发生的情况.

They mask intent. You can't look at the method call and understand what's going to happen.

您认为body()方法有什么作用?好吧,body是一个名词.拥有一个方法(应该是动词)作为名词会令人困惑.

What do you think the body() method does? Well, body is a noun. And having a method (which should be a verb) being a noun is confusing.

但是如果方法是age()怎么办.年龄既是动词又是名词.因此,当您看到$obj->age()时,是否要告诉对象它的年龄?还是您要告诉对象自己变老?

But what if the method was age(). Age is both a verb and a noun. So when you see $obj->age(), are you telling the object to give you its age? Or are you telling the object to age itself?

$obj->getAge()非常清楚您要执行的操作.而且$obj->getBody()同样清楚.

Whereas $obj->getAge() is crystal clear what you're trying to do. And $obj->getBody() is equally clear.

这增加了方法的复杂性.

It adds complexity to methods.

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

Your overall complexity will stay the same (because the same logic exists), but it shifts to fewer methods. Which means that instead of having 2 dead simple methods, you have one more complicated method.

是的,我会避免的.

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

But let's step back a minute. Rather than asking the question if "Single Function Accessors" are a bad habit, let's ask the same question about accessors in general...

我的回答是:是的.

它所依赖的是对象的角色以及特定属性的角色.这是视情况而定.

What it depends on is the role of the object, and the role of the specific property. It's a case-by-case basis.

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

There are tons of different types of objects (domain objects, services, proxies, collections, etc). Some are stateful, and some are not.

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

If the object isn't stateful, it has no properties and hence we can ignore it.

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

For those objects that have state, why do they have that state?

如果是因为它们代表国家,那么国家应该是公共的(不是说财产应该是公共的,而是国家应该暴露于外界).因此,代表业务实体的域模型应该具有公共状态.

If it's because they represent the state, then the state should be public (not saying the property should be public, but the state should be exposed to the outside world). So domain models which represent business entities should have public state.

class User {
    public $name;
}

但是,如果对象的作用不是代表状态,而是要对状态做一些事情,那么就不应公开对象.

But if the role of the object isn't to represent the state, but to do something with it, then it shouldn't be exposed.

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

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

In the mapper case, $pdo is incidental state. The mapper's job isn't to represent the data of the database connection, but it needs it to work.

最底线是从不公开对象未直接表示的状态.

The bottom line is never expose state that the object isn't directly representing.

仅因为您具有属性,并不意味着您应该公开它.实际上,很多时候您不应该公开它.这称为信息隐藏.

Just because you have a property, doesn't mean you should expose it. In fact, often times you shouldn't expose it. This is known as Information Hiding.

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

But what about types of objects that are stateful? Well, as it turns out there are two fundamental types of state:

  1. 应用状态

  1. Application State

请考虑配置以及类似的内容.基本上声明在构建时未定义,但在运行时已知.

Think Configuration and things like that. Basically state that's not defined at build time, but is known at runtime.

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

It's important to note that this state shouldn't change through the course of a request. And it also should be reasonably the same request-to-request (aside from deploys, etc).

用户状态

考虑状态或衍生自或取决于用户输入的数据.一个明显的例子是从表单提交的数据.

Think state and data that's derived or dependent upon user input. An obvious example would be data that was submitted from a form.

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

But a less obvious example would be if you use different renderers for different types of representations. So if a user requested a JSON response, the JSON rendering code being set in a property would be considered user state.

我的断言:

用于应用程序状态的属性访问器是错误的.应用程序状态没有理由在运行中更改,因此也没有理由必须传播该状态.

My assertion:

Property accessors for application state are bad. There's no reason that application state should change mid-run, therefore there's no reason that you should have to propagate the state.

用于用户状态 属性访问器可以.除此之外,还有更多.

Property accessors for user state may be OK. But there's more to it than that.

在您的示例中:

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

您实际上是将$body设为公共财产.

You're essentially making $body a public property.

没有没事.但是,为什么需要一种方法? public $body;到底有什么问题?

And there's nothing wrong with that. But why do you need a method? What's wrong with: public $body; in the first place?

有些人会说财产是邪恶的,永远不能公开!".这很费力,因为那正是您使用该访问器所做的.

Some people will say "properties are evil, they must never be public!". And that's hogwash, since that's exactly what you did with that accessor.

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

Now, if the accessor did some typing information (through type hints), or other validation logic (minimum length, etc), then that's a different story...

是吗?

让我举个例子.

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

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

Now, it should be clearly obvious that those properties are always valid. Right? Right? Right?

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

Well, no. What would happen if I created a child:

class LoosePerson extends StrictPerson {
    public function setName($name) {
        $this->name = $name;
    }
    public function setAge($age) {
        $this->age = $age;
    }
}

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

All of a sudden, all of our validation disappears. Now you could argue that was intended and it's the programmer's problem. Or you could simply change the properties to be private instead, to hold them always being valid. Right? Right? Right?

嗯,不.如果执行此操作会发生什么情况:

Well, no. What would happen if I did this:

$a = new StrictPerson;
$r = new ReflectionProperty($a, 'name');
$r->setAccessible(true);
$r->setValue($a, 'Bob');

我只是将一个无效值设置为应该始终有效的对象.

I've just set an invalid value onto an object that's supposed to always validate.

仅当您始终使用访问者时,才将访问者用作验证者.而且,您使用的每个工具都会始终使用它们.像mysqli和PDO以及Doctrine和PHPUnit之类的东西直接设置属性而不是调用setter会导致大量问题.

Using accessors as validators only works if you always use them. And every tool that you use always uses them. Things like mysqli and PDO and Doctrine and PHPUnit which set the property directly rather than calling setters can cause massive problems.

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

Instead, you can use an external validator:

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

那么,财产访问者是否有不良习惯?

我认为是的,很多时候这是个坏习惯.

So, Are Property Accessors A Bad Habit?

I argue that yes, often times they are a bad habit.

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

Now, there are some cases where you should use them:

  • 当您必须在接口中表示该状态时

  • When you must represent that state in an interface

在接口中未指定属性.因此,如果您必须表示该对象在接口中公开了状态,那么您应该使用getter和setter.

Properties are not specifiable in an interface. So if you must represent that the object exposes state in an interface, then you should use a getter and a setter.

强烈敦促您考虑为什么要创建界面.对于简单的数据对象,通常最好依赖于核心对象类型(由于数据对象上没有逻辑,因此多态性无法带来好处).

I'd strongly urge you to consider why you are creating the interface. For simple data objects, it's often better to rely on the core object type (since polymorphism can't benefit due to there being no logic on the data object).

由于某种原因需要隐藏内部表示形式时.

When you need to hide the internal representation for some reason.

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

An example would be a class that represents a unicode string. You may have an accessor method to get the raw value. But you'd want that to be a method so that you could convert the internal representation to a string of the proper character set.

创建访问器时,永远不要创建属性访问器.您应该改为创建状态访问器.区别在于属性访问者必须始终返回属性或对其进行设置.)

When you create an accessor, you should never create a property accessor. You should instead create a state accessor. The difference is that property accessors must always return a property or set it).

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

A state accessor on the other hand can "fake it" if it needs to. So our example above about the unicode string class, could internally represent the string as an array of code points. Then when you access the "string" state, it will convert the array into an actual string.

如果要使用访问器,应该将其抽象化.不代表它.

If you're going to use an accessor, it should be to abstract state. Not to represent it.

属性访问者是一个坏习惯.如果要这样做,请将属性设为公共并使用验证器对象.

Property accessors are a bad habit. If you're going to do that, make the properties public and use a validator object.

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

State accessors are not a bad habit. They are quite useful for maintaining useful abstractions.

这篇关于为什么单功能访问器似乎被认为是坏习惯?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆