12

I've been programming in procedural languages for quite some time now, and my first reaction to a problem is to start breaking it down into tasks to perform rather than to consider the different entities (objects) that exist and their relationships.

I have had a university course in OOP, and understand the fundamentals of encapsulation, data abstraction, polymorphism, modularity and inheritance.

I read https://stackoverflow.com/questions/2688910/learning-to-think-in-the-object-oriented-way and https://stackoverflow.com/questions/1157847/learning-object-oriented-thinking, and will be looking at some of the books pointed to in those answers.

I think that several of my medium to large sized projects will benefit from effective use of OOP but as a novice I would like to avoid time consuming, common errors.

Based on your experiences, what are these pitfalls and what are reasonable ways around them? If you could explain why they are pitfalls, and how your suggestion is effective in addressing the issue it'd be appreciated.

I'm thinking along the lines of something like "Is it common to have a fair number of observer and modifier methods and use private variables or are there techniques for consolidating/reducing them?"

I'm not worried about using C++ as a pure OO language, if there are good reasons to mix methods. (Reminiscent of the reasons to use GOTOs, albeit sparingly.)

Thank you!

3
  • 2
    not worthy of a complete answer, but an important one that took me a long time to accept (ie I read about it lots of time but treated it as fanboy talk): prefer free funtcions over member functions. This keeps your classes minimal which is a Good Thing.
    – stijn
    Commented Jun 21, 2011 at 6:43
  • @stijn Essentially you're saying if it doesn't need to be in the class, don't put it there. For example I see a lot of utility member functions that could easily be free functions in code I've read thus far.
    – Stephen
    Commented Jun 21, 2011 at 11:15
  • yes that's it. If you search for 'prefer non-member non-friend' you'll find a lot of information about it. In the end it comes down to adhering to the Single Responsability Principle
    – stijn
    Commented Jun 21, 2011 at 11:47

9 Answers 9

10

One big thing I've learnt is to design classes from the outside in. Design the interface before you even start thinking about the implementation. This will make the class much, much more intuitive for your users (those using the class) than writing the underlying algorithms and building up the class, and writing new public member functions as they are needed.

2
  • 7
    Adding to this, we can 'program by intention,' which is to say, write some sample code that would use the new class, see what types of methods and policies make it comfortable to use. Then use that information as you baseline for implementations.
    – jpm
    Commented Jun 21, 2011 at 2:56
  • 2
    Great in theory -- design interfaces and you are basically done. But it doesn't work that simple in practice. Interface design and implementation often go hand in hand in consecutive iterations until the final interface is crystallized. At that time you likely to have the final implementation as well. Commented Jun 21, 2011 at 7:16
9

Well the first is the pitfall of exposing too much information. The default should be private, not public.

After that comes too many getters/setters. Let's say I have a data member. Do I really need this data in an other class? Ok, make a getter. Do I really, reaally need to change this data during the lifespan of the object? Then make a setter.

Most novice programmers have the default to make a getter/setter for every data member. This clutters interfaces and are often bad design choices.

4
  • 3
    yes, it's quite popular among those who understood encapsulation superficially to make data private and then blow encapsulation with getters and setters. Commented Jun 21, 2011 at 3:40
  • Java is about the only popular language that still needs get/set methods. Every other language supports properties, so there is no syntactic difference between a public data member and a property. Even in Java, it is no sin to have public data members if you also control all the users of the class. Commented Jun 21, 2011 at 5:41
  • Public data members are harder to maintain. With getters/setters (or properties), you can keep the interface while changing the internal data representation, without changing any outside code. In languages where properties are syntactically identical to member variables from the consumer's point of view, this point doesn't hold though.
    – tdammers
    Commented Jun 21, 2011 at 10:40
  • So far I think I'm looking at private members "sort of" as local variables in a function ... the rest of the world doesn't need to know anything about them for the fn to operate ... and if the fn changes, only the interface needs to be consistent. I don't tend to go with spam getter/setter so I might be on the right track, in fact looking at a buffer class I wrote, there are no public data members at all. Thanks!
    – Stephen
    Commented Jun 21, 2011 at 12:31
2

When I crossed that chasm I settled on the following approach:

0) I started slow, with small/medium-size procedural apps, and no mission-critical stuff at work.

1) a simple 1st-pass mapping out of how I would write the program from scratch in OO style -- most importantly for me at that time, and this IS subjective -- was to figure out all the base classes. My object was to encapsulate as much as possible in the base classes. Pure virtual methods for everything possible in the base classes.

2) Then the next step was create the derivations.

3) the final step was -- in the original procedural code, separate the data structures from the obsever/modifier code. Then use data hiding and map all the common data into the base classes, and in the subclasses went the data that was not common across the program. And the same treatment for the procedural observer/modifier code -- all the 'used everywhere' logic went into the base classes. And view/modify logic that only acted on a subset of the data went into the derived classes.

This is subjective but it is FAST, if you know the procedural code and data structures well. A FAIL in a code review is when a bit of data or logic does not appear in the base classes but is used everywhere.

2

Take a look at other successful projects which use OOP to get a sense of good style. I recommend looking at Qt, a project which I always look up to when making my own design decisions.

1
  • Yes! Before a person becomes a master of something, he learns to imitate the great masters who worked before him. Studying good code others implemented is a good way to learn. I would recommend looking at boost, starting from simple designs and going deeper as one's understanding improves. Commented Jun 21, 2011 at 3:37
1

Depending on who you talk to, all data in OOP should be private or protected, and only available through accessors and mutators. In general, I think this is a good practice, but there are occasions where I diverge from this norm. For instance, if you have a class (in Java, let's say) who's only purpose is to bins some pieces of data into a logical unit, it makes sense to leave the fields public. If it's an immutable capsule, just mark them final and initialize them in the constructor. This reduces the class (in this instance) to little more than a struct (in fact, using C++, you should actually call this a struct. Works just like a class, but default visibility is public, and you intention is clearer), but I think you'll find that using it is much more comfortable in these cases.

One thing you definitely don't want to do is have a field that must be checked for consistency in the mutator, and leave it public.

3
  • 3
    I'd also suggest, if you have such classes that just hold public data, you should declare them as structs - it makes no semantic difference, but it clarifies the intention, and makes their usage seem more natural, especially to C programmers.
    – Dominic Gurto
    Commented Jun 21, 2011 at 2:57
  • 1
    Yeah, I agree here if you're in C++ (which the question did mention), but I do most of my work in Java, and we have no struct, unfortunately.
    – jpm
    Commented Jun 21, 2011 at 2:58
  • you need to clearly distinguish between aggregate classes (infrequent case) and classes with functionality (general case). The latter must practice encapsulation and access its members via public interface only, no data should be exposed public. Commented Jun 21, 2011 at 3:32
1

Kent Beck's book Implementation Patterns is an excellent grounding in how to use, not misuse, object-oriented mechanisms.

1

I'm not worried about using C++ as a pure OO language, if there are good reasons to mix methods. (Reminiscent of the reasons to use GOTOs, albeit sparingly.)

I didn't really think I had a lot to offer the conversation until I saw this bit. I have to disagree with the sentiment. OOP is just one of the paradigms that can and should be used in C++. Frankly, in my opinion it's not one of its strongest features.

From an OO standpoint I think C++ actually falls a bit short. The idea of having non-virtual functions for example is a tick against it in this respect. I've had arguments with those that disagree with me, but non-virtual members just don't fit the paradigm as far as I'm concerned. Polymorphism is a key component to OO and classes with non-virtual functions are not polymorphic in the OO sense. So as an OO language I think C++ is actually rather weak when compared to languages like Java or Objective-C.

Generic programming on the other hand, C++ has this one pretty good. I've heard it said there are better languages for this as well, but the combination of objects and generic functions is something that is quite powerful and expressive. Furthermore it can be damn fast both in programming time AND processing time. It's really in this area that I think C++ shines though admittedly it could be better (language support for concepts for instance). Someone thinking that they should stick to the OO paradigm and treat the others on the order of the goto statement in levels of immorality are really missing out by not looking at this paradigm.

The metaprogramming capacity of templates is also quite impressive. Check out the Boost.Units library for example. This library provides type support for dimensional quantities. I've made extensive use of this library in the engineering firm I currently work for. It just provides that much more immediate feedback for one aspect of possible programmer, or even specification error. It's impossible to compile a program that uses a formula where both sides of the '=' operator are not dimensionally equivalent without explicit casting. I personally have no experience with any other language in which this is possible, and certainly not with one that also has the power and speed of C++.

Metaprogramming is a purely functional paradigm.

So really, I think you're already stepping into C++ with some unfortunate misconceptions. The other paradigms besides OO are not to be avoided, they are to be LEVERAGED. Use the paradigm that is natural for the aspect of the problem you are working on. Don't force objects on what is essentially not an object prone problem. As far as I'm concerned, OO isn't even half the story for C++.

2
  • Doesn't the virtual keyword provide virtual class functionality in C++? As far as the goto comment, what I was driving at was that I'm not concerned about breaking empirical rules, as long as I understand the reasoning. I have, however, been looking further towards shunning imperative/procedural in favour of OO than may have been necessary. Thanks.
    – Stephen
    Commented Jun 21, 2011 at 11:09
  • virtual methods is not a prerequisite for polymorphism, it's just a common way of doing it. in fact, everything else equal, then making a method virtual actually weakens encapsulation, because you are increasing the size of the class' api, and you make it harder to make sure you follow liskov and so on. make something virtual only if you need a seam, some place to inject new behavior via inheritance (although inheritance is also something to be cautious of in OOP). virtual for virtual's sake does not make a class "more OOP"
    – sara
    Commented Apr 15, 2016 at 11:13
1

I wanted to accept an answer on this question, but I couldn't decide on one answer to bestow the check mark on. As such I've upvoted the original authors and created this as a summary answer. Thanks to everyone who took a few minutes, I found that the insight you provided gave me a good direction and a little reassurance that I wasn't off the rails.

@nightcracker

Well the first is the pitfall of exposing too much information. The default should be private, not public. After that comes too many getters/setters.

I felt that I had observed this problem in action in the past. Your comments made me also remember that by hiding the underlying variables and their implementation, I am free to change their implementation without destroying anything dependant on them.

Dominic Gurto

Design the interface before you even start thinking about the implementation. Gene Bushuyev Interface design and implementation often go hand in hand in consecutive iterations until the final interface is crystallized.

I thought Dominic's comment was a great ideal to aspire to, but I think that Gene's comment really hits the reality of the situation. So far I've seen this in action ... and feel a little better that it's not uncommon. I think that as I mature as a programmer I'll lean towards more complete designs, but right now I still suffer from jump in and get some code written-itis.

wantTheBest

I started slow, with small/medium-size procedural apps, and no mission-critical stuff at work. in the original procedural code, separate the data structures from the obsever/modifier code

This makes a lot of sense ... I liked the idea of keeping things working at work, but to refactor some of the non-critical stuff with classes.

jpm

One thing you definitely don't want to do is have a field that must be checked for consistency in the mutator, and leave it public

I've known for a while that this is one of the strengths of encapsulating the data... being able to enforce consistency and for that matter conditions/ranges/etc.

Crazy Eddie

Someone thinking that they should stick to the OO paradigm and treat the others on the order of the goto statement in levels of immorality are really missing out by not looking at this paradigm. The metaprogramming capacity of templates is also quite impressive.

I originally missed a lot in Crazy Eddie's answer, I think because I hadn't read up on some of the topics mentioned... like metaprogramming. I think the great message in CE's post was that C++ is such a blend of capabilities and styles that each should be used to their best potential... including imperative if that's what makes sense.

So again, thanks to everyone who responded!

0

The largest pitfall is the belief that OOP is a silver bullet, or the "one perfect paradigm."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.