Functional Decomposition

A development AntiPattern from the AntiPatternsBook.

Definition: In contrast to ObjectOriented programmers' practice of structuring a program based on an assortment of objects and how they interact, some StructuredProgramming programmers have been seen writing programs in OO languages without this emphasis on objects. This AntiPattern of FunctionalDecomposition is most often seen as the mistake of programming using entirely classes that contain one method that call other classes also with one method, as a total bypass the OO features of the language.

It happens when a non-OO design (possibly from legacy code) is coded in OO language and notation, but not using OO idioms. Imagine a C program being migrated to C++ using one class and a lot of private methods.

The solution is to essentially re-design and re-write the code.


AntiPattern? Says who? Using FunctorObjects is one of the most useful patterns in OOP.

I believe the intent of this page was not to deprecate FunctorObjects, which are indeed useful. Rather, it appears intended to attack a peculiar inversion of the GodClass. Instead of creating one class with all methods (i.e., a GodClass), you create one class per method. The deferred invocation implied by FunctorObjects is likely to be absent, or at best only deferred until the line immediately after the constructor invocation that instantiates the object. I have seen horrors like this:
  Splorp spl = new Splorp();
  spl.foob();
  Splat fzt = new Fzt();
  fzt.faz();
  Zot zt = new Zot();
  zt.zap();
...and so on, for hundreds of lines.

Even that's okay. One gains consistency and simplicity by using the same functor objects immediately in one circumstance that you might defer in another. Freedom to defer execution does not mean we must defer execution. But as you present the pattern above, it seems syntactically inconvenient. If it were instead:
   Splorp.new().foob();
   Fzt.new().faz();
   Zot.new().zap();
It would look a whole lot prettier, wouldn't it? Even better if we were weaving some parameters through the operations rather than relying on ambient state.
   Splorp.new(env).foob();
   Fzt.new(env,42).faz();
   Zot.new(env).zap("Hello");
If done with intent, the pattern represents something of value. It rarely comes from deliberate intent. It's more likely to be borne of ignorance, from a procedural programmer new to OO faced with a language like Java or C# that requires all procedure definitions to be associated with class definitions. If challenged, the usual explanation is either "it's the only way I could get it to work" or "I thought that was how OO is supposed to be done." Take a look at MarcGrundfest's comment below. It appears he's also seen this.

Rather than call it "bad", I would feel better if it was simply called "non-OO". ! OO != Bad. -- top

It's bad because OO languages often have poor non-OO tools, so you're using the language badly. Which is not to say that non-OO styles are bad.

In a mixed-paradigm world, such a tool/language is considered flawed. One must be careful to distinguish between "doing X is bad" versus "doing X in tool Y is bad".


This should have another name, since we need functional decomposition inside objects.

I see as a BadThing to name an AntiPattern alike a good practice when using other paradigms. --GeraldoXexeo


See http://www.fawcette.com/javapro/2002_03/magazine/columns/javatecture/ for discussion of a study into performance of code written following this AntiPattern compared to code written using OO idioms. Guess which comes out much faster.

It's not entirely clear from that article. The initial cut, which was dumb simple, did show that a pure FORTRAN-style implementation was faster. Of course the article states, "the object-oriented versions of the code were smaller, easier to read, and much easier to maintain, which can be important in a production environment." Further along in the article there is discussion of how to make OO fast. Better leave that to the original paper, though: http://www.cs.rice.edu/~zoran/Papers/linpack9.ps.gz

I get a postscript parse error when I try to extract this document. As far as I can tell it is a "lab-toy" kind of example that fiddles with math matrices. I was hoping for something more real-world.

When the hell was it established that procedural programming is an AntiPattern ?

Its only an AntiPattern If you think that you are doing OOD/OOP. It is not an AntiPattern If it is intentional and/or you have a good reason. Here good reason only means good by your criteria. Patterns and Antipatterns should really be evaluated with respect to a problem and perhaps an objective. If using OOP is not your objective then I agree it is not an AntiPattern. But I may still think it is a bad idea unless you are porting a procedural program. -- MarcGrundfest

Perfect Java programs can be written using static methods only, if it so pleases the developper in charge. Ditto for C++. There are countless practical examples of how C++ can be used as a "better C compiler", and sometimes that's what's needed.

Example?

Jake2 (http://bytonic.de/html/jake2.html), a port to Java of the (quite procedural) C source of the game Quake 2. Most of the methods are static, and many classes have just public properties. Interestingly, some Java proponents point to Jake2 as "evidence" that Java is fast enough for games.

{CeeIsNotThePinnacleOfProcedural}


Procedural programming doesn't have to be an AntiPattern - but it certainly can be in some contexts. -- Luke Closs

WhenToUseWhatParadigm


Also, FunctionalDecomposition is a poor name for procedural or non-OO programming. FunctionalDecomposition is a HeuristicRule for searching a large, combinatorially complex design space--or rather, for helping find a workable design without searching the whole space. The concept is simple: instead of trying out all combinations of all design variables, divide what you're trying to do into "functions" and design a component to fulfill each function. This yields smaller design spaces, which you can explore more successfully by using simple techniques like hill-climbing.

People exercise this HeuristicRule in nearly all designs, even software designs, even OO. For example, your refrigerator was probably designed independently from the rest of your kitchen. The refrigerator's power supply was probably designed and tested separately from the insulation, and both were probably designed and tested separately from the compressor. The most famous example of non-obvious FunctionalDecomposition was the suggestion, made by Sir George Cayley and later applied by the Wright brothers, of implementing lift and propulsion separately in an airplane. Prior to the Wright brothers, nearly everyone either tried to implement lift and propulsion with one component (e.g. flapping wings) or just didn't clearly separate those two functions in their explorations.

The big danger of FunctionalDecomposition is the belief that it's effective even for planning the design of things that you don't understand well enough to be sure of a given decomposition. There are always lots of ways to decompose a complex function into subfunctions. You can probably think of a lot of ways to keep food cold or fly people around. Most function decompositions fail because you can't feasibly develop a component to match each subfunction. For example, another way to decompose "fly people around" is into up/down motion, east/west motion, and north/south motion. If you could design mutually compatible components for each of those functions, you'd have a successful design, but so far no one has any idea how. In practice, most successful functional decomposition relies heavily on dividing into known kinds of component designs, and then just tweaking the latter designs. So much so that that's what "functional decomposition" means in practice.

If you do have a decomposition that's known to work, though, then it can be fantastic for planning. Perhaps most successful planning (of any sort) would be impossible without exactly this sort of decomposition.

-- BenKovitz

The up-down/east-west example is already flawed in that one may not have to have a different component for direction. Rather, "calculate vector" or something similar could perhaps be the function. But almost all connectivity of components, whether slicing is by nouns or verbs, requires conventions of expression to work together (compatible interfaces). Without such conventions, diverse components almost never fit well regardless of paradigm or slicing angle. In OOP, for example, you have the circle/ellipse problem in which there are different ways to represent circles (and other shapes). If one system uses approach 1 and a second uses approach 2, they won't fit well together. They are either incompatible, or require fat adapters. In my pet 2D drawing model, circles and ellipses are simply squares and rectangles with the smoothing attribute set to 100%. It would be tricky to attach that onto a radius-based approach. The adapters may just be more code than the shape handlers. -t


I'm surprised there is no mention of FunctionalProgramming in this discussion. If the paradigm is functional, especially if there is no need to keep track of the states of mutable variables, then this is no AntiPattern. If there is a need for a program to keep track of states, objects are usually the best way to keep track of the states, providing prescribed ways to manipulate/update the states. If instead the need is for an ability to process data in any of a multitude of ways, a functional paradigm is appropriate, because one isn't altering existing data/states, but rather is taking, for example, data A and creating report B. Creating B doesn't involve modifying or changing A. I agree it's an AntiPattern if one is using procedural logic in what are essentially monolithic objects, or creating single-method classes, or other awkward constructs, but those already have their own Antipattern descriptions.


Not to be confused with FunctionalComposition or anything else to do with FunctionalProgramming
See Also: StepwiseRefinement, ProceduralMethodologies
CategoryAntiPattern CategoryDevelopmentAntiPattern
FebruaryTwelve

EditText of this page (last edited March 1, 2012) or FindPage with title or text search