How much OOP is too much OOP?

Started by
22 comments, last by ToohrVyk 16 years, 11 months ago
Quote:Original post by Nypyren
Notice that there is no std::string::to_lower method to compete with the std::transform way of doing it. That's what I meant by bad OOP design. You have a very common operation and force the programmer to jump through bizarre hoops just to do something simple.

As far as the functional aspect goes, this pattern is sweet. I can replace std::tolower with anything that operates on the elements of the string (I could encrypt a container using the 5-argument version of std::transform and pass in two separate iterator pairs and an encryption functor). But as far as the lack of a well developed string class goes it is an unacceptable alternative.

What? None of that has anything to do with OOP. It's just functional programming, made slightly more difficult by C++'s container idioms and the lack of a string tolower function.
Advertisement
Quote:
But as far as the lack of a well developed string class goes it is an unacceptable alternative.

Should have picked a better example, then. The SC++L string class is a bit iffy; in conforming to the container interface, it ends up with a lot of extra member functions that aren't that useful for the common practice of "being a string," and lacks some of those functions that would be more useful (such as a native to_lower). But good arguments could be made for both high-level design choices, I don't think this is a case of "too much" (object obsessed) design at all, just a case of everyday design tradeoffs that in retrospect turned out to maybe not be so optimal.

Quote:
A better alternative in the functional style would have been to define a container interface (ala .Net), and a function signature like "std::transform(container, functor);"

It would look cleaner, but it would break the ability to use the SC++L algorithms on native types (such as arrays).
Quote:Original post by Sneftel
Quote:Original post by Nypyren
Notice that there is no std::string::to_lower method to compete with the std::transform way of doing it. That's what I meant by bad OOP design. You have a very common operation and force the programmer to jump through bizarre hoops just to do something simple.

As far as the functional aspect goes, this pattern is sweet. I can replace std::tolower with anything that operates on the elements of the string (I could encrypt a container using the 5-argument version of std::transform and pass in two separate iterator pairs and an encryption functor). But as far as the lack of a well developed string class goes it is an unacceptable alternative.

What? None of that has anything to do with OOP. It's just functional programming, made slightly more difficult by C++'s container idioms and the lack of a string tolower function.


I wasn't trying to focus on the functional code itself (it's debatable whether performing a mutable string operation is actually functional), I was trying to get people to wonder why there is not a tolower method on the std::string CLASS itself. std::string is a class, in a language touted for object oriented features, not for functional features. I apologize for not being painfully direct in my posts.
Quote:Original post by jpetrie
Quote:
But as far as the lack of a well developed string class goes it is an unacceptable alternative.

Should have picked a better example, then. The SC++L string class is a bit iffy; in conforming to the container interface, it ends up with a lot of extra member functions that aren't that useful for the common practice of "being a string," and lacks some of those functions that would be more useful (such as a native to_lower). But good arguments could be made for both high-level design choices, I don't think this is a case of "too much" (object obsessed) design at all, just a case of everyday design tradeoffs that in retrospect turned out to maybe not be so optimal.

Quote:
A better alternative in the functional style would have been to define a container interface (ala .Net), and a function signature like "std::transform(container, functor);"

It would look cleaner, but it would break the ability to use the SC++L algorithms on native types (such as arrays).


Obviously native "int foo[]" arrays don't have their own iterators in C++, either, because they are not a class. The iterators you use for them are external, providing access to native types, and as such there is nothing preventing you from also creating container objects that reference an underlying native type in the same manner.
Making all operations on an object into member functions isn't necessarily good OOP practice. Many people (myself included) think that anything that doesn't have to be a member function (i.e. doesn't need access to private fields) should be a free function. std::string has been repeatedly criticized for this, by Herb Sutter and others.
Free functions are fine with me as long as you organize them into namespaces. Nothing's worse than forgetting the name of a utility function that's out in the global namespace full of a few hundred thousand different definitions.

If you are a newbie programmer and you're using C, how do you first learn about the innumerable functions in the global namespace like strlwr? Probably looking at examples or browsing through MSDN or other online documentation. But how long does it take you to learn all of the useful functions? It could be months to years as you learn new APIs.


In .Net, you make your object, type its name, press '.', and pretty much everything that you can do with the class is immediately apparent in a concise auto-complete list. Sometimes you have to refer to documentation, but that's usually for the most advanced topics. You can learn everything about a class in a half hour, and start to get familiar with any classes that interoperate with it just as quickly. Without a graph-like organization your learning scope is quickly overwhelmed. Your ability to employ RAD is limited by your ability to know how to do what you want to do. If you don't already know then your best friend is OOP design that is easy to understand.

If you're completely new to an API, it's far easier to learn what objects provide particular functionality if it is present in the classes themselves. I've personally seen this to be one of the biggest productivity issues in professional game development, with the gigantic existing codebases that accumulate over a decade worth of "code re-use". No single coder is possibly going to remember where everything is, so you'd beter organize it so that anyone can quickly find what you need. It is not just your productivity on the line, it's everyone else's, too.
Sorry if this is inappropriate to this discussion, but by this time in a thread like this... I was fully expecting some response like this:

ANY OOP is too much OOP. The following is a fully functional Haskell program that you would be proud to show your mother


j[7]:=(*^)??:{]==
my_life:          nop          jmp my_life
[ Keep track of your TDD cycle using "The Death Star" ] [ Verge Video Editor Support Forums ] [ Principles of Verg-o-nomics ] [ "t00t-orials" ]
Quote:Original post by Nypyren
Free functions are fine with me as long as you organize them into namespaces. Nothing's worse than forgetting the name of a utility function that's out in the global namespace full of a few hundred thousand different definitions.


Now you're being inconsistent. std::transform is in a namespace - 'std'.

Quote:In .Net, you make your object, type its name, press '.', and pretty much everything that you can do with the class is immediately apparent in a concise auto-complete list.


No; in your IDE, that happens. Intellisense exists in C++. It's not very good, but that's because of the horrible warts on the language grammar left over from its C legacy, not because of how the library is organized.

Quote:Your ability to employ RAD is limited by your ability to know how to do what you want to do.


Some would argue that RAD isn't accomplished by typing in code, full stop...
There's a simple rule. What's should be an object, should be an object. What shouldn't, shouldn't. In this regard, Java is flawed. The writers of the Java standard library really hate verbs. Instead of a function called 'ProcessElements', they have a class called 'ElementProcessor'.
Quote:Original post by Nypyren
Obviously native "int foo[]" arrays don't have their own iterators in C++, either, because they are not a class. The iterators you use for them are external, providing access to native types, and as such there is nothing preventing you from also creating container objects that reference an underlying native type in the same manner.


Native arrays do have an iterator type. It is an element pointer.

int a[size];

sort(a,a+size);

This topic is closed to new replies.

Advertisement