Ever wonder why the C++ STL uses an external, non-member function model for implementing algorithms for containers? The following article sums it up. Although a few of the “no less efficient” examples are debatable, the underlying principles definitely have merit.
One downside to the functional approach outlined in the article–that I didn’t see mentioned–is the fact that the interface is actually being split in a few cases, which is actually detrimental to cohesion. This happens when you have multiple overloaded forms of a function, and you keep one “flexible” one as a member function, but make the others external functions. Why not keep those as members for the sake of consistency? You can still implement them in terms of the one “flexible” member function, thereby continuing to ensure good encapsulation.
A few thoughts on applying the spirit of the law discussed in the article to other languages:
- C# – Use extension methods, but the “this” type should be as generic as possible. You still get the benefits of DRY but in a more object-oriented fashion that works well with Intellisense and cohesion. I like the extension method approach better than the old static-methods approach (e.g., string.Join) because it still enforces good encapsulation, and is less verbose and more cohesive than the alternative.
- Ruby – Use mix-ins that use duct-type delegation to other methods. These can be automatically mixed into all applicable classes when the mix-in is “required”.
- Lisp – Brilliant! All you have are functions!
It’s also important to note the power you derive from combining this functional approach with lambda functions, function objects, and covariance.