30

当我使用 getter 或 setter 时,这里的原则经常粘贴到我这里,但人们告诉我不要使用它们。该网站清楚地解释了我应该做什么和不应该做什么,但它并没有真正解释为什么我应该告诉而不是询问。我发现使用 getter 和 setter 效率更高,而且我可以用它们做更多事情。


想象一个Warrior具有属性health和的类armor

class Warrior {
    unsigned int m_health;
    unsigned int m_armor;
};

现在有人用特殊攻击攻击我的战士,降低他的护甲 5 秒。使用 setter 会是这样的:

void Attacker::attack(Warrior *target)
{
    target->setHealth(target->getHealth() - m_damage);
    target->setArmor(target->getArmor() - 20);
    // wait 5 seconds
    target->setArmor(target->getArmor() + 20);
}

并且告诉,不要问原则它看起来像这样(如果我错了,请纠正我):

void Attacker::attack(Warrior *target)
{
    target->hurt(m_damage);
    target->reduceArmor(20);
    // wait 5 seconds
    target->increaseArmor(20);
}

现在第二个显然看起来更好,但我找不到它的真正好处。您仍然需要相同数量的方法(increase/ decreasevs set/ get),并且您失去了询问是否需要询问的好处。例如,如何将战士的生命值设置为 100?您如何确定是否应该使用healor hurt,以及需要多少健康才能治愈或伤害?

此外,我看到一些世界上最好的程序员正在使用 setter 和 getter。大多数 API 都使用它,并且它一直在 std lib 中使用:

for (i = 0; i < vector.size(); i++) {
    my_func(i);
}
// vs.
vector.execForElements(my_func);

如果我必须决定是相信这里的人链接我一篇关于告诉而不是询问的文章,还是相信 90% 成功的大公司(苹果、微软、安卓、大多数游戏等)赚了很多钱和工作程序,我有点难以理解为什么会告诉,不要问是一个好原则。

当 getter 和 setter 似乎一切都变得更容易时,我为什么要使用它(我应该吗?)?

4

4 回答 4

32

您仍然需要相同数量的方法(增加/减少与设置/获取),并且您失去了询问是否需要询问的好处。

你理解错了。关键是将getVariableand替换setVariable为有意义的操作:inflictDamage例如。替换getVariableincreaseVariablejust 为 getter 和 setter 提供了不同的更晦涩的名称。

这有什么关系。例如,您不需要提供 setter/getter 来以不同的方式跟踪护甲和生命值,inflictDamage可以通过尝试阻挡(并在此过程中损坏盾牌)然后对角色造成伤害来处理单个如果屏蔽不够或您的算法需要它。同时,您可以在一个地方添加更复杂的逻辑。

例如,添加一个魔法护盾,可以在短时间内暂时增加你的武器造成的伤害。如果您有 getter/setter,所有攻击者都需要查看您是否有这样的项目,然后在多个地方应用相同的逻辑以希望得到相同的结果。在告诉方法中,攻击者仍然需要弄清楚他们造成了多少伤害,然后告诉你的角色。然后角色可以弄清楚伤害是如何分布在物品上的,以及它是否以任何其他方式影响角色。

使游戏复杂化并添加火焰武器,然后您就可以拥有inflictFireDamage(或将火焰伤害作为不同的参数传递给inflictDamage函数)。可以判断她是否受到火焰抗性法术的Warrior影响并忽略火焰伤害,而不是让程序中的所有其他对象尝试弄清楚他们的行为将如何影响其他对象。

于 2013-05-19T14:40:35.827 回答
2

好吧,如果是这样,为什么还要麻烦 getter 和 setter 呢?你可以只拥有公共领域。

void Attacker::attack(Warrior *target)
{
    target->health -= m_damage;
    target->armor -= 20;
    // wait 5 seconds
    target->armor += 20;
}

原因很简单。封装。如果你有 setter 和 getter,它并不比公共领域好。您不在这里创建结构。您创建具有定义语义的程序的适当成员。

引用文章:

这里最大的危险是,通过从对象请求数据,您只是获取数据。你没有得到一个对象——不是在广义上。即使您从查询中收到的东西在结构上是一个对象(例如,一个字符串),它在语义上也不再是一个对象。它不再与其所有者对象有任何关联。仅仅因为你得到一个内容是“RED”的字符串,你就不能问这个字符串是什么意思。是业主姓氏吗?车的颜色?转速表的现状?一个对象知道这些事情,数据不知道。

这里的文章在这里建议“告诉,不要问”在这里更好,因为你不能做没有意义的事情。

target->setHealth(target->getArmor() - m_damage);

这在这里没有意义,因为盔甲与健康无关。

另外,你在这里弄错了 std lib。Getter 和 setter 仅用于,std::complex这是因为语言缺乏功能(当时 C++ 还没有引用)。其实恰恰相反。C++ 标准库鼓励使用算法来告诉容器上要做的事情。

std::for_each(begin(v), end(v), my_func);
std::copy(begin(v), end(v), begin(u));
于 2013-05-19T14:19:07.173 回答
1

想到的一个原因是能够决定您想要控制的位置。

例如,在您的 setter/getter 示例中,调用者可以任意更改 Warrior 的健康状况。充其量,您的设置器可能会强制执行最大值和最小值以确保运行状况保持有效。但是,如果您使用“告诉”表单,您可以强制执行其他规则。您可能一次不允许超过一定数量的伤害或治疗,等等。

使用这种形式,您可以更好地控制 Warrior 的界面:您可以定义允许的操作,并且可以更改它们的实现,而无需重写所有调用它们的代码。

于 2013-05-19T14:20:16.297 回答
0

在我看来,两个代码都做同样的事情。不同之处在于每个人的表现力。第一个(setter anad getters)比第二个(告诉,不要问)更具表现力。

确实,当您提出要求时,您将做出决定。但这在大多数情况下不会发生。有时您只是想知道或设置对象的某些值,而这对于tell, don't ask是不可能的。

当然,当您创建程序时,重要的是定义对象的职责并确保这些职责仅保留在对象内部,让您的应用程序的逻辑脱离它。我们已经知道这一点,但是如果您需要要求做出不属于您的对象的责任的决定,您如何通过告诉做出决定,不要问

实际上,getter 和 setter占了上风,但通常会看到tell 的想法,不要与它一起问。换句话说,有些 API 有 getter 和 setter 以及tell 的方法,不要问idea。

于 2013-05-19T14:33:25.207 回答