Jump to content

  • Log In with Google      Sign In   
  • Create Account


What reasonable amount of accessor functions do you need in a C++ class?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
10 replies to this topic

#1 tom_mai78101   Members   -  Reputation: 568

Like
0Likes
Like

Posted 28 April 2011 - 03:42 PM

I tend to notice that if you have a class for a certain requirement, chances are there will be a lot of accessor methods wrapped nicely in the C++ class itself. (Possibly Java)

I'm curious to know what is the reasonable amount of accessor methods for a class with any one of the following requirements:

1. Game engine: How many are there, do you think that's reasonable enough for maintenence and easier scripting?
2. Mutator methods: Since modifying a variable requires pair(s) of accessor methods, I wondered if we could control the amount of them while implementing a class?
3. Nested accessor methods: Just in case, if C++ allows you to invoke a mutator method via an accessor method, how do we reduce the amount of accessor methods?

Thanks in advance.

Sponsor:

#2 Ravyne   Crossbones+   -  Reputation: 6904

Like
5Likes
Like

Posted 28 April 2011 - 04:13 PM

You should have very few (ideally none) pure mutators -- greater encapsulation is achieved when any state is maintained only through the use of methods which perform some higher-level concept, rather than for there to be the possibility that state is directly modified from the outside.

This is not to say that its always wrong to have state which is directly modifiable from the outside, sometimes its simply the right tool for the job. However, better designs should tend to shy from such things and an over-reliance on externally-modifiable state, especially between classes which are not otherwise logically coupled in the design space, would indicate that the developer has probably failed to demarcate the lines of responsibility clearly. More plainly stated, and over-reliance on such pure mutators is a code-smell.

Most people, I think, fall into this trap when they design their classes around data, rather than around responsibilities. Think of a class first as the role it fulfills in the design, what methods it needs to perform its work, and only towards the end begin to think about what state it needs to maintain to achieve those goals and what state is should expose to clients.

Therefore, there is no budget or allowance that can be recommended to you. Try to avoid pure mutators and accessors as best you can, use them when it *really* makes sense, and don't loose any sleep over it.

#3 tom_mai78101   Members   -  Reputation: 568

Like
0Likes
Like

Posted 28 April 2011 - 04:54 PM

Here are a few thoughts I just conceived upon reading your post.

I really hate mutators/acessor methods quite a bit, due to its bloating-the-class disadvantage. But then, having few mutators and using methods with higher-level of concepts makes the class itself bloated with a lot of higher-level methods in order for the class functions to achieve its goals in a more generic way.

Unless having a bloated class is pretty normal, and very accepting in the industry, using encapsulation wrapping around functions' responsibilities seems like one of the unheard-of, well-known coding practices at school. Thus, I need to do a research on my part.

Am I correct to say that a class should not have a lot of responsibility-encapsulated, higher-level methods and never a lot of mutators/accessor methods, instead, go and "divide-and-conquer" a class into multiple classes with hierarchy-ness in-between (making all of the classes related in some way)?

#4 Nanoha   Members   -  Reputation: 296

Like
0Likes
Like

Posted 28 April 2011 - 05:15 PM

I've noticed as I get better at designing/coding my classes have gone from usually having many accessors/mutators to having almost none - objects should just "do things". Too many accessors is, in my opinion bad design. Whats too many completely depends so that itself is not very useful. I wouldn't be afraid of bloating the class with methods, I'd be afraid of bloating its interface (which many accessors/mutators do). If you can encapsulate parts of an object in further objects (and combine with composition) then even better. Just my opinion of course.

I find it quite rare to use accessors/mutators, usually the constructor is enough to setup the object once. If I do need alot of access, such as creating a loader type object (and not wanting to add loading functionality directly to the class) I'll just friend the loadeer object rather than making alot of accessor/mutators.

#5 Ravyne   Crossbones+   -  Reputation: 6904

Like
3Likes
Like

Posted 28 April 2011 - 06:53 PM

What language are you using / do you intend to use? This can sometimes make a rather large difference in the way you implement an interface, or, indeed, even what is or is not a part of the interface. In Java, for instance, you are forced into the mentality that "everything is a class, or belongs to a class" -- that is, there are no free-standing functions. C# is similar, but has the concept of "extension methods" which exist as part of a class outside of the class they operate on, and can only use that class's public interface, but which appear, syntactically to be a part of the class it operates on (This is how they implemented the LINQ stuff, for example). Then there's C++, where free-standing functions in the same namespace as a class are considered to be a part of the class's interface, due to the way lookup resolution works (Koenig Lookup).

Then you have still more dimensions which affect interface design -- does your language support anonymous functions (the cool kids call them lambdas)? Those can sure be used in a lot of creative ways that can provide flexibility without breaking encapsulation. What about C++ Templates/Java Generics/CLR Generics? Same idea, very different implementations, and different capabilities.


None of that probably helps you -- in fact, it might give your head cause to spin, but these are some of the things you think about while dreaming up an interface.

As for generic design advice, I think I didn't quite communicate clearly when I said to design in terms of "higher-level concepts" in that "high-level" might have implied methods which do a lot of work-- it would have been more clear for me to say to design in terms of "actions" -- think in terms of the things you want to "do" first, and don't get bogged down early in terms of what state is required to achieve it. If we think first of classes as a collection of actions/abilities, its easier to see how classes should be broken down into subclasses and what relationships they hold with other classes, than if we think of classes as a collection of data.

Ideally a class should do precisely one "logical unit" of work or represent one conceptual entity (often through composition of smaller, more-focussed classes). In software engineering this is known as the Single Responsibility Principle, or SRP for short. The essence of SRP is that each unit of functionality, whether it be data, methods, classes, modules, or entire libraries should fill exactly one well-defined role. Obviously, when we talk about data or methods a "single responsibility" probably best corresponds to a single, atomic unit of work -- you can think of these as the "leaves" of your program. As we move up the "branches" of the program a "single responsibility" becomes something which is still "singular" at that conceptual level, but which is implemented in terms of the "leaves" which do all the detailed work. Likewise, the "trunk" of the tree pulls all those branches together to do one grand, useful thing by leveraging all the code in those branches and leaves. Whatever conceptual level you are working at, you are always thinking about one responsibility -- its just that the scope of responsibility increases as higher-level concepts are built on the backs of lower-level concepts. You should always strive to keep the idea of "one responsibility" in mind.

The Liskov Substitution Principle, or LSP for short, is also something that you should always bare in mind. LSP basically says to use inheritance only when one class"is-a(n)" more-refined version of the another class, and that composition should be preferred when one class "has a(n)" (or, "is-made-of") another class. Inheritance is sometimes (ab)used to locate some common code in a base class -- which is the most common violation of this principle.

In general, less is more in the world of programming. Smaller interfaces and small inheritance hierarchies demonstrate strong encapsulation and cohesion. Seasoned programmers, at least the good ones, develop an instinct about how requirements break down into the necessary components, as well as a sort of gut-feeling that things are veering off-course when its faults start to show through. If you keep at it, you will be there one day too, but for now you should read and understand the research to help guide your designs.

#6 nfries88   Members   -  Reputation: 259

Like
0Likes
Like

Posted 28 April 2011 - 07:08 PM

I tend to notice that if you have a class for a certain requirement, chances are there will be a lot of accessor methods wrapped nicely in the C++ class itself. (Possibly Java)

I'm curious to know what is the reasonable amount of accessor methods for a class with any one of the following requirements:

1. Game engine: How many are there, do you think that's reasonable enough for maintenence and easier scripting?
2. Mutator methods: Since modifying a variable requires pair(s) of accessor methods, I wondered if we could control the amount of them while implementing a class?
3. Nested accessor methods: Just in case, if C++ allows you to invoke a mutator method via an accessor method, how do we reduce the amount of accessor methods?

Thanks in advance.


Alot of accessor methods: Direct mutators (direct access to modify a member variable) are considered bad OOP because encapsulation of that data allows you to pre-process changes. The same is true with accessors, but post-processing instead. An example of this would be if you were writing a game GUI library. When you want to set the position of a widget, it is considerably more efficient to update its position members relative to the position of the widget containing that widget (panel, window, etc) than it is to calculate the widget's position on screen at render / event handler time. If you care about unnecessary optimization, anyway. Realistically, integer math operations are so fast it shouldn't even touch your framerate even with hundreds or thousands of widgets drawn each frame. It's an example relative to something I'm working on right now, so that's just the first thing that came to mind. Another thing might be updating a player's hp in the game -- generally you'll use a widget to display the player's health. Depending on how you made that work, you may need to update the value in the mutator.

But, realistically, it's up to you to decide whether you want to encapsulate your data. If you know you only need direct access to it, go crazy.

1) If I understand you correctly, you mean a game controlling class (IE the "Game" class in the XNA framework). You shouldn't need mutators and accessors for that, merely behavioral functions.
2) Don't think of limiting them. If they're in header files, they will already be inlined if the compiler thinks they're worthy, which effectively means that there is no function call overhead. If not, then clearly they're really necessary.
3) Nope. Just don't be stupid and cause an infinite recursion

EDIT: In 99 out of 100 cases, Ravyne is completely right in the post above this one.
Looking for paid or open-source C++ programming work. Been programming since 2005. No degree.

#7 Ravyne   Crossbones+   -  Reputation: 6904

Like
1Likes
Like

Posted 28 April 2011 - 07:08 PM

Also, if you're not already -- learn how to use "const" correctly to ensure that client code doesn't have unexpected access to your class internals. Use it on parameters, use it on return values, use it on method signatures (const methods) use it on pointer declarations (constant pointers, pointers to const data, and const pointers to const data) -- everywhere you can that is appropriate. Constness is every bit as much a part of the interface as the method calls themselves. It helps clients know what they can or cannot do, discourages unintended side-effects, and also gives the compiler a little more leeway to perform optimizations.

I use it religiously. So should you. So should everyone.

#8 owl   Banned   -  Reputation: 364

Like
0Likes
Like

Posted 28 April 2011 - 08:30 PM

I only use getters. If the member variable needs to be read and modified as is by *any* class I drop it public and that's it. There is no point in bloating the declaration with accessors to achieve the same result as making the variable publicly accessible.
I like the Walrus best.

#9 tom_mai78101   Members   -  Reputation: 568

Like
0Likes
Like

Posted 28 April 2011 - 10:06 PM

Thanks again, everyone.

My preferred languages are C/C++ and Java. Since they both use classes, all of the information I get in this thread are invaluable.

#10 DevFred   Members   -  Reputation: 836

Like
0Likes
Like

Posted 29 April 2011 - 12:11 AM

My preferred languages are C/C++ and Java. Since they both use classes

There is no language called "C/C++", and C certainly does not have classes.

#11 mhagain   Crossbones+   -  Reputation: 7565

Like
0Likes
Like

Posted 29 April 2011 - 03:22 AM

Generally you need one for each member that needs one.

I am being serious here; there is no fixed amount that is prescribed under any particular circumstances; your code follows your design and not some abstract rules like "you need X amount of thing Y". If your design requires 200, then you need 200. If your design requires 1 then you need 1. Simple as that.

It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS