好吧,就在我读到的每个地方,我都读到了吸气剂/孵化器是"邪恶的".现在,作为一个经常在PHP/C#中使用getter/setter的程序员,我看不出他们是如何活着的.我已经读过他们打破了封装等等,但是,这是一个简单的例子.
class Armor{
int armorValue;
public:
Armor();
Armor(int); //int here represents armor value
int GetArmorValue();
void SetArmorValue(int);
};
Run Code Online (Sandbox Code Playgroud)
现在,让我们说吸气剂和制定者是"邪恶的".如何在初始化后更改成员变量?
例:
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?
Run Code Online (Sandbox Code Playgroud)
让我们说无论出于何种原因,上述情况都不合适.如果我的游戏将护甲值限制在1到500之间怎么办?(没有护甲可以拥有超过500护甲或少于1护甲的护甲).
现在我的实施成了
void Armor::SetArmor(int tArmValue){
if (tArmValue>=1 && tArmValue<=500)
armorValue=tArmValue;
else
armorValue=1;
}
Run Code Online (Sandbox Code Playgroud)
那么,如果不使用getter/setter,我将如何施加此限制?如果不使用getter/setter,我还能如何修改属性?armorValue应该是案例1中的公共成员变量,还是案例2中使用的getter/setter?
好奇.多谢你们
hyd*_*yde 15
你误会了什么.不使用getter/setter会打破封装并暴露实现细节,并且对于某些邪恶定义可能被视为"邪恶".
我猜他们在某种意义上可以被认为是邪恶的,如果没有适当的IDE /编辑器支持,他们在某种程度上是用C++编写的tediois ...
C++的一个缺陷是创建非const引用getter,它也允许修改.这与返回指向内部数据的指针相同,并将锁定内部实现的那部分,并且实际上并不比将字段公开更好.
编辑:基于评论和其他答案,您听到的内容可能是指始终 为每个字段创建非私有的getter和setter .但我也不会称之为邪恶,只是愚蠢;-)
Jer*_*fin 10
稍微逆势:是的,getter和setter(又名存取函数)是大多是邪恶的.
这里的邪恶不是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;
}
};
Run Code Online (Sandbox Code Playgroud)
有了这个,定义一些范围为1..500的护甲变得完全无足轻重:
bounded<int> armor(1, 500);
Run Code Online (Sandbox Code Playgroud)
根据具体情况,您可能更喜欢定义(例如)saturating尝试分配超出范围值的类型,但实际分配的值将是范围内最接近的值.
saturating<int> armor(1, 500);
armor = 1000;
std::cout << armor; // prints "500"
Run Code Online (Sandbox Code Playgroud)
当然,我上面给出的内容也有点勉强.对于你的盔甲类型,它可能很方便支持-=(并且可能+=),因此攻击最终会像x.armor -= 10;.
底线:getter和setter的(或至少"一个")主要问题是它们通常指向你已经将变量定义为一种类型,当你真的想要一些其他类型的碰巧类似于几种方式.
现在,某些语言(例如,Java)确实无法为程序员提供编写这样的代码所需的工具.在这里,我相信你使用C++标签来表明你确实想要编写C++.C++确实为您提供了必要的工具,并且(至少IMO)您的代码将更好地利用它提供的工具,因此您的类型强制执行所需的语义约束,同时仍然使用干净,自然,可读的语法.
| 归档时间: |
|
| 查看次数: |
4157 次 |
| 最近记录: |