好的,就在我读到的每个地方,我都读到了吸气剂/孵化器是“邪恶的”。 现在,作为一个经常在PHP / C#中使用getter / setter的程序员,我看不出他们是如何活着的。我已经读过他们打破了封装等等,但是,这是一个简单的例子。
class Armor{
int armorValue;
public:
Armor();
Armor(int); //int here represents armor value
int GetArmorValue();
void SetArmorValue(int);
};
现在,让我们说吸气鬼和二传手是“邪恶的”。 如何在初始化后更改成员变量。
示例:
Armor arm=Armor(128); //armor with 128 armor value
//for some reason I would like to change this armor value
arm.SetArmorValue(55); //if i do not use getters / setters how is this possible?
让我们说无论出于何种原因,上述情况都不合适。 如果我的游戏将护甲值限制在1到500之间怎么办。(没有护甲可以拥有超过500护甲或少于1护甲的护甲)。
现在我的实施变为
void Armor::SetArmor(int tArmValue){
if (tArmValue>=1 && tArmValue<=500)
armorValue=tArmValue;
else
armorValue=1;
}
那么,如果不使用getter / setter,我还会如何施加此限制呢? 如果不使用getter / setter,我还能如何修改属性? armorValue应该是案例1中的公共成员变量,还是案例2中使用的getter / setter?
好奇。谢谢人们
答案 0 :(得分:15)
你误会了什么。 不使用getter / setter打破封装并暴露实现细节,并且对于某些邪恶定义可能被视为“邪恶”。
我猜他们在某种意义上可以被视为邪恶,如果没有适当的IDE /编辑器支持,他们在某种程度上可以用C ++编写...
C ++的一个缺陷是创建非const引用getter,它也允许修改。这与返回指向内部数据的指针相同,并将锁定内部实现的那部分,并且实际上并不比将字段公开更好。
编辑:根据评论和其他答案,您听到的内容可能是指始终为每个字段创建非私有的getter和setter。但我也不会称之为邪恶,只是愚蠢; - )
答案 1 :(得分:10)
略微逆势:是的,吸气者和煽动者(又名访问者和变异者) 主要是邪恶的。
这里的邪恶不是IMO,而是“打破封装”,因为从简单地将变量定义为一种类型(例如,int
)时,它真的不那种类型。看看你的例子,你将Armor称为int
,但事实并非如此。虽然它无疑是一个整数,但肯定不是 int
,其中(除其他外)定义了一个范围。虽然您的类型是整数,但它根本不打算支持与int
相同的范围。如果您希望Armor
属于integer from 1 to 500
类型,请定义直接表示该类型的类型,并将Armor
定义为该类型的实例。在这种情况下,由于您要强制执行的不变量被定义为类型本身的一部分,因此您无需在其上添加setter以尝试强制执行它。
template <class T, class less=std::less<T> >
class bounded {
const T lower_, upper_;
T val_;
bool check(T const &value) {
return less()(value, lower_) || less()(upper_, value);
}
void assign(T const &value) {
if (check(value))
throw std::domain_error("Out of Range");
val_ = value;
}
public:
bounded(T const &lower, T const &upper)
: lower_(lower), upper_(upper) {}
bounded(bounded const &init)
: lower_(init.lower), upper_(init.upper), val_(init.val_)
{ }
bounded &operator=(T const &v) { assign(v); return *this; }
operator T() const { return val_; }
friend std::istream &operator>>(std::istream &is, bounded &b) {
T temp;
is >> temp;
if (b.check(temp))
is.setstate(std::ios::failbit);
else
b.val_ = temp;
return is;
}
};
有了这个,定义一些范围为1..500的护甲变得完全无足轻重:
bounded<int> armor(1, 500);
根据具体情况,您可能更愿意定义(例如)saturating
类型,其中尝试分配超出范围的值很好,但是实际分配的值将只是范围内最近的值。
saturating<int> armor(1, 500);
armor = 1000;
std::cout << armor; // prints "500"
当然,我上面给出的内容也有点勉强。对于你的护甲类型,支持-=
(可能还有+=
)可能会很方便,因此攻击最终会像x.armor -= 10;
那样。
底线:getter和setter的(或至少“一个”)主要问题是它们通常指向你已经将变量定义为一种类型,当你真的想要其他类型碰巧是某种类型的在某些方面类似。
现在,某些语言(例如Java)确实无法为程序员提供编写这样的代码所需的工具。在这里,我相信你使用C ++标签来表明你确实想要编写C ++。 C ++确实为您提供了必要的工具,并且(至少IMO)您的代码将更好地利用它提供的工具,因此您的类型强制执行所需的语义约束,同时仍然使用干净,自然,可读的语法。 / p>
答案 2 :(得分:5)
简而言之:他们不是邪恶的。
它们长并没有错,因为它们不会泄漏内部表示。我觉得这里没有问题。
答案 3 :(得分:2)
对get / set函数的一个常见批评是它们可能被客户端代码滥用以执行逻辑上应该封装在类中的操作。例如,假设客户想要&#34;擦亮&#34;他们的盔甲,并决定效果是增加&#34;价值&#34;到了20岁,所以他们做了他们的小小的设定并且很开心。然后其他地方的其他客户端代码决定生锈的装甲应该将值降低30,并且他们做了他们的位。与此同时,客户代码中的十几个其他地方也允许对装甲进行抛光和生锈效果 - 以及说&#34;强化&#34;和&#34;破解&#34;,并直接实施它们。对此没有中央控制......盔甲类的维护者没有能力做这样的事情:
每件盔甲最多涂一次锈,抛光,增强和裂缝效果
调整特定逻辑效果的值或从中减去的数字
决定新的&#34;皮革&#34;装甲类型不会生锈,并且忽略客户试图使其这样做
另一方面,如果第一个想让盔甲生锈的客户不能通过界面这样做,他们就会去装甲班的维护者那里说'嘿嘿,给我一个执行此操作的功能&#34;,然后其他人可以开始使用逻辑级&#34; rust&#34;操作,如果它后来变得有用,我可以在盔甲类中轻松地集中实现它们(例如,通过使用单独的布尔来说明盔甲是否生锈,或者记录生锈的单独变量)效果)。
因此,具有get / set函数的东西是它们阻碍了逻辑功能API的自然演变,而是在整个客户端代码中分配逻辑,导致在extremis 中导致无法维护的混乱。
答案 4 :(得分:1)
你的getter / setter看起来不错。
getter / setter的替代方法是将成员变量公开。更准确地说,将变量分组到没有成员函数的结构中。并在你的课堂上运用这个结构
答案 5 :(得分:1)
访问成员会减少封装,但有时需要。最好的方法是通过吸气剂和制定者。有些人在没有必要进行此类访问时实施它们,只是因为它们可以并且这是一种习惯。
答案 6 :(得分:1)
好的吸气者会这样做:
答案 7 :(得分:0)
如果不使用getter / setter,我还会如何施加此限制?如何在不使用getter / setter的情况下修改属性?
您可以检查从变量中读取的内容,如果其值超出范围,请使用预定义值(如果可能)。
您还可以使用脏兮兮的黑客攻击,例如保护变量下面的内存不会写入,捕获写入尝试以及禁止/忽略具有无效值的内存。这实现起来很麻烦并且执行起来很昂贵。但是,它可能对调试很有用。