C++ Getter/Setter (Alternatives?)
C++ Getter/Setter (Alternatives?)
好吧,几乎在我读到的地方,我都读到getters/setters是"邪恶的"。现在,作为一个经常在 PHP/C# 中使用 getter/setter 的程序员,我看不出它们是如何活着的。我读过它们会破坏封装等,但是,这里有一个简单的例子。
class Armor{
int armorValue;
public:
Armor();
Armor(int); //int here represents armor value
int GetArmorValue();
void SetArmorValue(int);
};
现在,让我们说getters和setter是"邪恶的"。初始化后应该如何更改成员变量。
例:
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 的情况下修改属性?在案例 1 中,armorValue 应该只是一个公共成员变量,而在案例 2 中是否应该使用获取者/设置者?
好奇。谢谢伙计们
你误解了什么。不使用 getter/setter 会破坏封装并暴露实现细节,对于邪恶的某些定义,可以被认为是"邪恶的"。
我想从某种意义上说,它们可以被认为是邪恶的,如果没有适当的 IDE/编辑器支持,它们在C++中编写起来有点乏味......
C++的一个缺陷是创建非常量引用getter,这也允许修改。这与返回指向内部数据的指针相同,并且会锁定内部实现的那部分,并且实际上并不比公开字段更好。
编辑:根据评论和其他答案,您听到的可能是指始终为每个字段创建非私有getter和setter。但我也不会称之为邪恶,只是愚蠢;-)
稍微相反:是的,获取者和设置者(又名访问器和突变者)大多是邪恶的。
IMO,这里的邪恶不是来自"打破封装",而是简单地将变量定义为一种类型(例如,int
),而实际上它根本不是那种类型。看看你的例子,你称 Armor 为int
,但事实并非如此。虽然它无疑是一个整数,但它肯定不是一个int
,它(除其他外)定义了一个范围。虽然类型是整数,但它根本不打算支持与int
相同的范围。如果希望Armor
属于类型 integer from 1 to 500
,请定义一个类型来直接表示该类型,并将Armor
定义为该类型的实例。在这种情况下,由于要强制实施的不变量被定义为类型本身的一部分,因此无需在其上附加资源库来尝试强制执行它。
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)您的代码会更好地利用它提供的工具,因此您的类型强制执行所需的语义约束,同时仍然使用干净,自然,可读的语法。
简而言之:他们不是邪恶的。
只要他们不泄露内部表示,他们就没有错。我认为这里没有问题。
对 get/set 函数的一个常见批评是,客户端代码可能会滥用它们来执行逻辑上应该封装在类中的操作。 例如,假设一个客户想要"抛光"他们的盔甲,并决定效果是将"价值"增加 20,所以他们做了他们的小事情并很高兴。 然后其他地方的其他客户端代码决定生锈的装甲应该将值降低 30,他们尽自己的一份力量。 同时,客户端代码中的其他十几个地方也允许对装甲进行抛光和生锈效果 - 以及"加固"和"开裂",并直接实现它们。 对此没有中央控制...装甲类的维护者没有能力做这样的事情:
-
每件装甲最多应用一次锈蚀、抛光、加固和裂纹效果
-
调整添加到或减去值的数字以获得特定的逻辑效果
-
决定新的"皮革"装甲类型不会生锈,并忽略客户试图这样做
另一方面,如果第一个想要使 armour 生锈的客户端无法通过界面做到这一点,他们会去找 armour 类的维护者并说"嘿,给我一个函数来做这件事",那么其他人可以开始使用逻辑级别的"rust"操作,如果以后做我上面描述的那种事情变得有用,它们可以在 armour 类中轻松集中地实现(例如,通过一个单独的布尔值来表示装甲是否生锈,或者一个单独的变量记录生锈效应)。
因此,get/set 函数的问题在于它们挫败了逻辑功能 API 的自然演变,而是在整个客户端代码中分发逻辑,导致极端情况下导致无法维护的混乱。
你的getter/setter看起来不错。
getter/setter 的替代方法是将成员变量公开。更准确地说,将变量分组到没有成员函数的结构中。并在你的class
向成员授予访问权限可以减少封装,但有时这是必要的。最好的方法是通过吸气手和二传手。有些人在不需要这种访问时实施它们,只是因为它们可以并且这是一种习惯。
何时,Getters都是邪恶的:
- 它们直接访问类的数据成员
- 每次向类添加数据时都必须添加新的 getter 时
- 每个吸气器的数据行为都不同
因此,好的获取者会做到以下几点:
- 他们将请求转发到其他对象或从多个位置收集数据
- 您只需使用一个 getter 即可获取大量数据
- 您获取的所有数据的处理方式相同
另一方面,二传者总是邪恶的。
否则我将如何在不使用 getter/setter 的情况下施加此限制?否则,如何在不使用 getter/setter 的情况下修改属性?
您可以检查从变量中读取的内容,如果其值超出范围,请使用预定义的值(如果可能)。
您还可以诉诸肮脏的黑客,例如保护变量下方的内存不被写入,捕获写入尝试以及禁止/忽略具有无效值的尝试。这将很难实现,而且执行起来很昂贵。不过,它可能对调试很有用。
- 如何为 C 型字符串数组编写 getter 和 setter?
- C++ Setter/Getter,cout 工作,printf 失败
- 当要访问的对象被多次封装时,如何正确使用setter
- C++中大多数/所有 setter 函数的参数是否应该写为常量引用?
- 为什么我的 setter 方法会产生错误的访问错误
- 我们可以有一个 setter 成员函数作为从 const 对象引用的 const 吗?
- Getter 和 Setter 用于类 C++ 中的数组元素
- 如何编写C++getter和setter
- 常量成员和没有setter的私有成员之间有什么区别
- 如何在没有setter的情况下设置类内部类的成员变量?
- 正确构造和销毁 Setter 依赖注入对象(可能使用 qt)
- 如果构造函数和析构函数可以获取和显示(打印)数据,为什么我们需要 getter 和 setter?
- 在不编写显式 setter 的情况下修改私有类数据成员的便捷方法是什么?模板有用吗?
- 有没有办法在没有括号的情况下在C++中调用成员的getter/setter?
- Getter and Setter for a View
- Boost python getter/setter with the same name
- 公共变量是否比使用 getter 和 setter 更快?
- 没有默认构造函数的对象成员的 wig setter
- C++ - 调用 setter 的函数 - 如何装饰参数
- C++ Getter/Setter (Alternatives?)