My apologies if a variation of this question has been asked before, but due to its nature it is hard to search for.

I am having a discussion with a colleague about object-oriented design, and it basically comes down to a choice between two different paradigms. What I had been searching Google for was a discussion between these - as I've seen both discussed.

Paradigm A

Have a small number of classes which answer a lot of public methods, many of which are not applicable unless in certain circumstances. In these cases, they throw a runtime exception.

Example: a mathematical vector class can have any number of elements. This class answers 'Cross', but returns an error if it has anything other than 3 elements.

Paradigm B

A deeper inheritance tree, with methods only appearing where they are applicable.

Example: the mathematical vector class is specialised as a 3D-vector, with only the 3D-vector answering 'Cross'. 3D vectors are a special case as there are many functions relevant to a positional vector alone (Dot, Cross, Distance, AngleTo, etc).

I'm not actually asking for opinions on the above (although clearly the second is better!) but where can I find a good discussion on this - and similar - design issues. I can find many beginners' discussions/tutorials on the subject, but I haven't had any luck finding the more in-depth articles.


After a prompt from @JohnDibling, I'll rephrase the question (and add another Paradigm on request):

Paradigm C

Lightweight objects with a suite of functions to interpret them, and throw errors if the provided arguments weren't valid.

Example: all vectors only respond to the most basic requests, and a series of functions like Size(vector), Cross(vector) do all of the work. Cross would throw a (runtime) error if an argument of the wrong size is given.

Could anyone give me specific reasons why A or C might be preferable to B, or point me to an article which discusses this issue in depth?

  • 2
    I don't like either, but A is so bad it should not even be considered. Concerning B, why should vectors of different dimensions inherit from some vector class? Do you really have code that should treat them the same? – juanchopanza Aug 13 '13 at 13:17
  • Get a design patterns book, such as amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/…. (And the second isn't clearly better, like all design patterns there are instances where it is useful and instances where it is not and using one in all instances is not viewing the problem from all angles) – MadScienceDreams Aug 13 '13 at 13:18
  • What is the actual question you're asking here? – Mark B Aug 13 '13 at 13:21
  • You forgot paradigm C: A shallow inheritance tree, with methods only appearing where they are applicable. – molbdnilo Aug 13 '13 at 13:27
  • @MarkB - I was hoping for a recommendation for either books or better still online articles that discuss this sort of design issue. All of my searches are flooded with articles like "what is Polymorphism?", so although they are undoubtedly out there, they are hard to find :-( – Mike Sadler Aug 13 '13 at 13:28

It depends on what you're trying to accomplish.

Generally when you use OOP or any other architectural technique you want to leverage the benefits that it's approach facilitates.  One of those benefits may be more reuse.  Reuse is considered good because it can reduce the overall load of code that must be reviewed,code that must be written, etc.

The OOP architectural paradigm also offers information hiding and encapsulation, important for managing and reducing complexity.  Reducing complexity in code is crucial to improving code quality and increasing overall development speed.

In my opinion, one of the best ways to refine your underlying OOP architecture is to begin to build a reference implementation.  Try to actually implement the high level functionality, at least conceptually or in pseudo code.  Doing this forces you to continue the architectural pattern you begin with the fundamental class definitions.  In this way you can get feedback from your implementation attempts about what OOP patterns are working cleanly and clearly to express your higher level functionality and which are hindering those efforts by making things more difficult by enforcing unnecessary conformance or, conversely, allowing excessive, distracting and confusing abstraction.

Known (GOF...) design pattern can help you by providing expert heuristics about which things have worked for many people over many trials.

But ultimately it is your project's requirements which will have to guide the final OOP foundation construction.

The core concept for me is whether a particular architectural approach is going to have a NET benefit or a NET loss.  If it takes longer to create a whole boatload of abstraction concepts and work out all the kinks in your inheritance hierarchy, that it would to just do a more quick and dirty approach and fix a few bugs or issues. Well, it's a judgement call.

Also, it depends on what you plan to do,  if these classes are intended to be the foundation of another 100,000 lines of OOP class hierarchy definitions, it may be time well spent to thoughtfully refine the abstractions as tightly as possible.

| improve this answer | |

What about option C: A mathematical vector with a sparse interface in a namespace that provides free-functions such as Cross that accept any vector you wish but throws an exception if it doesn't meet pre-conditions?

See also one of the fine books at the SO book list: https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list

| improve this answer | |
  • +1 Or fails to compile if some conditions are not met. – juanchopanza Aug 13 '13 at 13:25
  • Yep, that's another one. Could you recommend a book or better still online article that discusses these things comprehensibly and well? – Mike Sadler Aug 13 '13 at 13:26

Well I would say that neither are good ^^

Basically because the first paradigm do not follow the Liskov substitution principle.

(See the wikipedia link for some good references ...)

As a designer, you must try to limit coupling. As inheritance is one of the strongest link possible, you should avoid it whenever possible, see Herb Sutter on the subject (part 2) for example.

The second smells bad as it seems to do that sort of thing. By the way, you will end up with vector4D, vector5D, ...

modern design try to use inheritance sparsely. Most design patterns [GoF] do exactly that ! They try to limit the coupling between classes, replacing inheritance relationship with something else ^^

| improve this answer | |
  • Ah, that is one article that I had found! If I understood LSP (which I probably haven't) in my example above, the 3D vector would answer everything the general vector does, and therefore be subsitutable according to LSP, wouldn't it? – Mike Sadler Aug 13 '13 at 13:24
  • @Mike: Well, yes and no. If you have a base interface Vector which does not define Cross, and then you define Cross only for the Vector3D class, you can use a Vector3D anywhere as a vector from an interface point of view. But when you add some new methods to a subclass you can possibly modify the invariants. That's why it usually a code smell to do that when you want to follow the principle. In fact, when you add methods, it usually means that you use subclassing to factorize things. Not the best design decision. See Herb Sutter article. I will amend my answer. – neuro Aug 13 '13 at 13:40
  • Thanks @neuro - that Herb Sutter article is exactly the sort of thing I'm after. – Mike Sadler Aug 13 '13 at 13:53
  • @Mike: You're welcome. Browse Sutter other articles. I suggest his books as well as the effective C++ series by Scott Meyers. – neuro Aug 13 '13 at 13:56
  • I've actually got his book on my desk! I've read it through about a year ago, but I might re-read it in the light of my last year's learning. PS: the reason a 3D vector is special is because it represents a point in space, and has about 15 additional methods unique to it (Cross, Dot, DistanceTo, etc). Only physicists are likely to care about the 4D and 5D ones ;-) – Mike Sadler Aug 13 '13 at 14:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy