C++ C++;Getter/Setter(替代方案?)
好吧,几乎我读到的每一个地方,我都读到能手/二传手是“邪恶的”。 现在,作为一个经常在PHP/C#中使用getter/setter的程序员,我看不出它们是如何存在的。我已经读到他们破坏了封装等等,然而,这里有一个简单的例子C++ C++;Getter/Setter(替代方案?),c++,C++,好吧,几乎我读到的每一个地方,我都读到能手/二传手是“邪恶的”。 现在,作为一个经常在PHP/C#中使用getter/setter的程序员,我看不出它们是如何存在的。我已经读到他们破坏了封装等等,然而,这里有一个简单的例子 class Armor{ int armorValue; public: Armor(); Armor(int); //int here represents armor value int GetArmorValue(); void SetArmorV
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;
}
void Armor::SetArmor(int tArmValue){
如果(tArmValue>=1&&tArmValue您误解了某些内容。不使用getter/setter会破坏封装并暴露实现细节,对于某些邪恶的定义,可以将其视为“邪恶”
我猜想他们在某种意义上可以被认为是邪恶的,没有适当的IDE编辑器支持,他们在C++中有点写…
一个C++陷阱是创建非const参考吸引子,它也允许修改。这与返回指向内部数据的指针一样,并且将锁定内部实现的那部分,并且确实不比生成字段公共。
编辑:根据评论和其他答案,你听到的可能是指总是为每个领域创建非私有的getter和setter。但我也不会称之为邪恶,只是愚蠢;-)简言之:它们不是邪恶的
只要它们不泄露内部表示,就没有问题。我看这里没有问题。你的getter/setter看起来不错
getter/setter的替代方法是公开成员变量。更准确地说,将变量分组到没有成员函数的结构中。并在类中对该结构进行操作给成员访问减少了封装,但有时这是必要的。最好的方法是使用getter和setter。有些人们在不需要访问的情况下实现它们,只是因为它们可以,而且这是一种习惯
如果不使用getter/setter,我将如何施加此限制?如果不使用getter/setter,我将如何修改属性
您可以检查从变量读取的内容,如果其值超出范围,请改用预定义值(如果可能)
您也可以使用肮脏的黑客手段,例如保护变量下面的内存不被写入,捕获写入尝试,以及禁止/忽略具有无效值的尝试。这将是一个很麻烦的实现过程,执行起来也很昂贵。不过,它可能对调试有用。无论何时:
它们直接访问类的数据成员
每次向类添加数据时都必须添加新的getter
每个getter中的数据行为都不同
因此,优秀的获取者将执行以下操作:
它们将请求转发给其他对象或从多个位置收集数据
您可以使用一个getter获取大量数据
您获取的所有数据都以相同的方式处理
另一方面,二传者总是邪恶的。有点逆向:是的,获取者和二传者(又名访问者和变异者)大多是邪恶的
在我看来,这里的罪恶与其说来自“破坏封装”,不如说来自简单地将变量定义为一种类型(例如,int
)当它真的不是那种类型时。看看您的示例,您将Armor称为int
,但它实际上不是。虽然它无疑是一个整数,但它肯定不是int
,这(除其他外)定义一个范围。虽然您的类型是一个整数,但它根本不支持与int
相同的范围。如果您希望Armor
的类型是从1到500的整数,请定义一个直接表示该类型的类型,并将Armor
定义为该类型的实例。在这种情况下,由于要强制执行的ant被定义为类型本身的一部分,您不需要在其上附加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;
}
};
当然,我上面给出的也有点简单。对于您的装甲类型,支持-=
(可能还有+=
)可能会很方便,因此攻击会以x.armor-=10;
结束
一句话:getter和setter的主要问题(或者至少是“一个”)是,它们通常指向您定义了一个变量作为一种类型,而实际上您需要某种其他类型的变量,而这种类型恰好在一些方面类似
<>现在,有些语言(例如java)不能给程序员提供编写代码所需的工具。这里我相信C++的标记使用表明你确实想写C++。C++确实为你提供了必要的工具,(至少IMO)。您的代码将更好地利用它提供的工具,这样您的类型将强制执行所需的语义约束,同时仍然使用干净、自然、可读的语法。对get/set函数的一个常见批评是,客户端代码可能会滥用它们来执行逻辑上应该封装在类中的操作。例如,假设一个客户想要“抛光”他们的盔甲,并决定其效果是增加“价值”到了20岁,他们就开始做自己的小动作,很开心。然后其他地方的其他客户代码决定rusty armour的值应该降低30,他们也尽了自己的一份力。与此同时,客户代码中的其他十几个地方也允许对armour进行抛光和生锈处理,以及说“加固”和“开裂”,并直接实施。这方面没有中央控制……装甲级的维护者没有能力做以下事情:
- 有锈迹,
bounded<int> armor(1, 500);
saturating<int> armor(1, 500);
armor = 1000;
std::cout << armor; // prints "500"