Monday, April 13, 2009

A Plethora of Paradigms

paradigm - a pretentious and overused term for a way of thinking (Bjarne Stroustrup, "C++ Glossary")

C++ is frequently described (by Stroustrup and others) as a multi-paradigm programming language, which simply means that it lets you use more than one programming paradigm (such as procedural or object-oriented) and freely switch between or combine paradigms. When I was first introduced to this concept of multi-paradigm programming languages, I thought that it was an incredibly cool trait of C++. Then I read on Wikipedia that the multi-paradigm label can be applied to many more languages than I'd realized and that C++ is a relative lightweight at only three paradigms. (The current record holder, supporting eight Wikipedian-recognized paradigms, is a language I've never heard of called Oz.) However, C++ is probably still the best known example of this sort - there are even ">books about it - and has some of the most practical multi-paradigm applications. A quick overview of C++'s paradigms:

First, of course, is the structural or procedural programming paradigm. Procedural programming's pretty passé, so there's not much to be said here, except to note that C++'s multi-paradigm nature lets you incrementally migrate a procedural app to an OO design.

Second is object-oriented programming. When OO first went mainstream, it was billed as a way of achieving reuse in software development. This doesn't seem to have quite worked out as advertised; creating truly reusable software is hard regardless of the technology you're using, and while OO design is critical to frameworks from MFC to RoR, framework development isn't where most programmers live. Even if its ability to grant reuse per se is limited, modern OO design is quite good at permitting software to be flexible, extensible, and stable in the face of change. "Modern OO design" means taking full advantage of guidelines like the SOLID principles and the Law of Demeter rather than just applying the basic OO techniques of encapsulation, inheritance, and polymorphism. It also includes knowing when to bend those guidelines if circumstances call for it.

C++'s third paradigm is generic programming. I find this to be harder to get a handle on... It's much less commonly taught than object-oriented programming, and although I've worked in C++ for years, it's only been in the last year or two that I've really started to "get" generic programming. (A good test to see how well you understand generic programming is to browse the Boost libraries and see how many of them you think you could, in theory, implement.) Generic programming makes the STL possible; it makes it possible to add features like dimensional analysis, lambda expressions, and callbacks to C++; it's a major component in adding user-defined types of all sorts. It also creates some rather heinous error messages...

However, generic programming isn't just for STL containers and the metaprogramming wizards at Boost; it overlaps with and can partially replace OO techniques. OO programming is known for permitting software components to be loosely coupled (such that they can be freely combined and freely changed, within limits, without the effects of those changes rippling out to other components), through polymorphism and interfaces. Generic programming also permits loose coupling, except that it's handled entirely at compile time, with no runtime overhead, and it doesn't require that objects be related through an inheritance hierarchy. The Curiously Recurring Template Pattern is probably the simplest and best known example of this (and is even referred to as compile-time or static polymorphism), but it's far from the only example. Most of part four, for example, of Imperfect C++ contains different applications of generic programming techniques to simulate what might require inheritance, interfaces, or (C#) extension methods in the OO world.

For even more power, the generic and OO paradigms can be combined. And because C++ is such a flexible language, you're not limited to the "officially supported" paradigms. I've already touched on Boost's support for functional programming; as another example, reflection (which is available through vendor extensions, like C++Builder's or the .NET Framework's and through third party libraries like CERN's Reflex) can also promote flexible, extensible software. Having three different techniques from three different paradigms that can be used individually or in various combinations offers incredible power: power to shoot yourself in the foot by creating an overcomplicated, obtuse rat's nest of code, or power to write robust, extensible software. Now if I could only figure out which category my code falls in...

No comments: