获取器和设置器风格

23
< p >(撇开是否应该使用它们这个问题。)< /p> < p >我一直更喜欢只使用函数重载来为获取器和设置器提供相同的名称。< /p>
int rate() { return _rate; }      
void rate(int value) { _rate = value; }

// instead of     
int getRate() { return _rate; }      
void setRate(int value) { _rate = value; }

// mainly because it allows me to write the much cleaner     
total( period() * rate() );    
// instead of      
setTotal( getPeriod() * getRate() );

我自认为是正确的,但我想知道库的作者是否有任何好的理由?


27
“自然而然,我是正确的。” 这相当有趣,我希望这是故意开玩笑的。 - Ed S.
13个回答

33

我更喜欢使用get / set版本,因为它更清楚地说明了正在发生的事情。如果我看到rate()和rate(10),我怎么知道rate(10)不仅仅是在计算中使用10来返回利率?我不知道,所以现在我必须开始搜索才能弄清楚发生了什么。一个函数名称应该只做一件事情,而不是两个相反的事情。

另外,正如其他人指出的那样,有些人喜欢省略'get'并保留'set',即

int Rate( );
void SetRate( int value );

那种约定非常明确,我读起来不会有任何问题。


2
好的观点。假设使用您的代码的人不仅是一个知道您住在哪里的杀人狂,而且还有点困惑! - Martin Beckett

6

我一直喜欢省略我的getter方法中的“get”,就像你使用rate()而不是getRate()一样。但是对于setter方法重载,这对我来说似乎不是一个非常好的想法,因为名称rate并没有传达对象正在被改变的信息。请考虑:

total(period() * rate()); // awesome, very clear

rate(20); // Looks like it computes a rate, using '20'...but for what?  And why ignore the return value?

这是Qt和Java风格。http://qt.gitorious.org/qt/pages/ApiDesignPrinciples ---> 转到“命名艺术”。 - Ronny Brendel

6

那么int rate();void setRate(int value);如何?这种方式可以避免同名函数执行不同的操作,并且仍然可以使用period() * rate()


1
可能会导致人们寻找 getXXX 时混淆。 - Martin Beckett
这是Qt和Java风格。http://qt.gitorious.org/qt/pages/ApiDesignPrinciples ---> 转到“命名艺术”。 - Ronny Brendel

5
几年前,我完全同意这种做法。但最近开始有了疑虑,因为这会使得获取器(getter)或设置器(setter)的地址变得不明确。使用像tr1::bind这样的工具会很麻烦。
例如:
struct A
{
   void Foo(int);
   int Foo()const;
};

std::vector<A> v = ....;
std::vector<int> foos;
// Extract Foo
std::transform(
   v.begin(), v.end(), 
   std::back_inserter(foos), 
   //Ambiguous
   // std::tr1::bind(&A::Foo)
   //must write this instead. Yuck!
   std::tr1::bind(static_cast<int(Foo::*)()>(&A::Foo));
);

略过是否应该拥有它们的问题;)

bind()和更糟糕的bind2nd()是让我认为C++已经无法挽回的东西。没有人可以合理地期望解析,更不用说正确地编写那行代码了! - Martin Beckett
1
好观点。重载本身很好,但当你想开始获取函数地址时,它会妨碍你。- @mgb:不要低估同胞们。人们也可以使用std::mem_fun_ref,但由于重载的存在,类型必须指定为:std::mem_fun_ref<int, A>(&A::Foo) - UncleBens

4

我想提醒一下,这应该是一个社区维基问题。

当我开始学习C ++时,我寻找了样式指南,Google的某些方面很好:

  • 方法大写(只是更漂亮)。
  • getter简单和小写(rate)。
  • setter明确和小写(setRate)。

2

还有一个未被其他人提到的问题,那就是函数重载的情况。看看这个(编造和不完整的)例子:

class Employee {
    virtual int salary() { return salary_; }
    virtual void salary(int newSalary) { salary_ = newSalary; }
};

class Contractor : public Employee {
    virtual void salary(int newSalary) {
        validateSalaryCap(newSalary);
        Employee::salary(newSalary);
    }
    using Employee::salary; // Most developers will forget this
};

如果没有使用using子句,由于重载,Contractor的用户无法查询薪资。最近我在我工作的项目中将-Woverloaded-virtual添加到警告集中,结果这种情况随处可见。


你不知道MSVC中等价于-Woverloaded-virtual的选项吗? - Greg Domjan
1
@Greg 抱歉,我不知道。实际上,我发现 MSVC 的警告(截至 VC8)大多数都是适得其反的(例如 bool 和 int 之间的转换性能警告?之前作为 class 前向声明的结构体?)。由于我在两个平台上构建,所以通常依赖于 gcc 来满足我所有的警告需求。 - Tom

2

简洁明了很重要,但不应牺牲完整性或误导性。因此,我更喜欢使用GetFoo()和SetFoo()而不是Foo()和Foo(int foo)。


2
"Getting"和"Setting"有几个不同的层次。
  • 我使用Get和Set进行“快速”操作。
  • 如果某些操作需要较长时间才能执行,则通常会使用Calc,因为这些名称意味着必须完成一些工作才能检索结果。
  • 对于较长时间的操作,您开始进入前缀,如Load/Save、Query/Store、Read/Write、Search/Find等。

因此,Get/Set可以被赋予有用的含义,并成为一个更大的、一致的命名策略的一部分。


1

虽然Ed的评论是正确的,但我更喜欢实际属性而不是setter/getter反模式。当你的域图中有三分之一的方法是由eclipse生成的虚拟方法时,就有问题了。

然而,如果没有一流的属性,我认为这种反模式是最有意义的。

此外,它使代码完成更容易。

obj.set (control shift space) 用于设置器
obj.get (control shift space) 用于获取器


为什么当一等属性只是相同概念的语法糖时,它会成为“反模式”?概念是相同的,只是实现不同而已。 - Ed S.
这是一种反模式,因为它可以变成一行代码。私有属性 int 值; //现在可以使用 IoC 容器了;耶!“我在所有函数的顶部重复了 30 行代码。这怎么可能是反模式呢?代码能正常工作啊!” - Stefan Kendall
我认为他的意思是拥有getter/setter本身就是一种反模式——这并不一定是正确的——请参见我对jmucchiello的评论。 - Martin Beckett

1

个人认为,成对出现的getter和setter是从“可视化”语言及其“属性”中继承而来的代码味道。在一个“好”的类中,数据成员应该是只写或只读的,而不是可读/可写的。

我认为最常见的getter和setter的原因是没有深入到对象模型中。在你的例子中,为什么要将总期限和利率传递给total?它们不是类的成员吗?所以你只需要设置期限和利率,然后只获取总数。

可能有例外,但我讨厌看到一个类并发现“getX/setX, getY/setY等等”。这似乎表明作者没有考虑如何使用类,而是使类易于访问数据,以便他不必考虑类应该如何使用。

当然,我是正确的。


想象一个三维点类型。它将有许多内部函数来执行各种数学运算,但最终需要所有6个获取/设置x/y/z函数。 - Martin Beckett
@mgb:就我个人而言,我不认为一个3D点类型应该首先隐藏成员(除非你可能想使用数组作为内部表示)。 - UncleBens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接