getters and setters style

2019-02-07 20:35发布

问题:

(Leaving aside the question of should you have them at all.)

I have always preferred to just use function overloading to give you the same name for both getter and setters.

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() );

Naturally I am correct, but i wondered if the library writers had any good reason ?

回答1:

I would prefer the get/set versions because it is more clear as to what is going on. If I saw rate() and rate(10), how do I know that rate(10) isn't simply using 10 in the calculation to return the rate? I don't, so now I have to start searching to figure out what is going on. A single function name should do one thing, not two opposing things.

Also, as others have pointed out, some prefer to omit the 'get' and leave the 'set', i.e.,

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

That convention is pretty clear as well, I wouldn't have any problem reading that.



回答2:

How about int rate(); and void setRate(int value);? This has the virtue of not having two functions of the same name doing different things, and still allows period() * rate().



回答3:

I have always preferred to omit the 'get' on my getters, as you do, with rate() instead of getRate(). But overloading for the setter does not seem like a very good idea to me, since the name rate doesn't convey that the object is being mutated. Consider:

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?


回答4:

A few years ago, I would have agreed completely. More recently, a doubt began to make its way, because that makes taking the address of a getter or setter ambiguous. With tools like tr1::bind, this is really annoying.

For example:

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));
);

Leaving aside the question of should you have them at all ;-)



回答5:

I'll go ahead and mention this should be a community wiki question.

When I started learning C++ I looked for style guides, and Google's was good for some points:

  • Methods in uppercase (it's just prettier).
  • getters plainly and lowecase (rate).
  • setters explicitly and lowercase (setRate).


回答6:

Being concise is important, but not at the cost of being incomplete or misleading. For that reason, I prefer GetFoo() and SetFoo() to Foo() and Foo(int foo).



回答7:

There are several levels to "Getting" and "Setting"

  • I use Get and Set for "fast" operations.
  • If something will take a longer time to execute, then it will often be a Calc, as these names imply that some work has to be done to retrieve the result.
  • For longer operations, you start to get into prefixes like Load/Save, Query/Store, Read/Write, Search/Find etc.

So Get/Set can be ascribed a useful meaning, and be part of a larger, consistent naming strategy.



回答8:

While Ed's comment is true, I do prefer actual properties over the setter/getter antipattern. When 1/3rd of the methods in your domain graph are dummy methods that have been generated by eclipse, there's something wrong.

Without first class properties, however, I believe the antipattern makes the most sense.

Furthermore, it makes code completion easier.

obj.set (control shift space) for setters
obj.get (control shift space) for getters



回答9:

Personally, I think getters and setters found in pairs are a code smell carried over from "visual" languages and their "properties". In a "good" class, the data members are writeonly or readonly but not read/write.

I think the most common cause of getters and setters is not carrying the object model deep enough. In your example, why is total being passed the period and the rate? Aren't they members of the class? So you should only be setting the period and the rate and you should only be getting a total.

There are probably exceptions but I just hate looking at a class and finding "getX/setX, getY/setY, etc. etc." It just seems there wasn't enough thought put into how the class SHOULD be used and rather the author made the class EASY to get at the data so he wouldn't have to consider how the class should be used.

Naturally I am correct.



回答10:

Another issue no one else has mentioned is the case of function overloading. Take this (contrived and incomplete) example:

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
};

Without that using clause, users of Contractor cannot query the salary because of the overload. I recently added -Woverloaded-virtual to the warning set of a project I work on, and lo and behold, this showed up all over the place.



回答11:

I enforce the convention in which a method should always be a verb and a class should always be a noun (except for functors, for obvious reasons). In which case, a get/set prefix must be used for consistency. That said, I also agree entirely with Ed Swangren. This sums to me as using those prefixes a no-brainer.



回答12:

I prefer to avoid the get and set labels, the information is not needed for the compiler to do it's job for most of these simple properties.

you can have issues:

class Stuff {
  void widget( int something ); // 'special' setter
  const Widget& widget( int somethingelse ) const; // getter
}
Stuff a; 
a.widget(1); // compiler won't know which widget you mean, not enough info


回答13:

If your getter is simply rate(), your compiler would complain that its a redefinition of your other rate symbol, provided you gave your field a good meaningful name like that. In that case you need to do something silly like name your member _rate or some other similar approach. I personally hate seeing/typing those underscores, so tend to go with the getRate() approach.

This is obviously subjective and this just happens to be my personal preference.