5

Android developers probably are familiar with Ceja's Clean Architecture, where use cases are classes that implement the Command Pattern.

Shvets defines the pattern intents as follows:

  • Encapsulate a request as an object, thereby letting you parametrize clients with different requests, queue or log requests, and support undoable operations.
  • Promote "invocation of a method on an object" to full object status
  • An object-oriented callback

I use that approach in order to improve code readability and testability. But, after reading Shvets's Anti-Patterns course, I got confused with his Functional Decomposition Anti-Pattern definition:

  • Classes with "function" names such as Calculate_Interest or Display_Table may indicate the existence of this AntiPattern.
  • All class attributes are private and used only inside the class.
  • Classes with a single action such as a function.
  • An incredibly degenerate architecture that completely misses the point of object-oriented architecture.
  • Absolutely no leveraging of object-oriented principles such as inheritance and polymorphism. This can be extremely expensive to maintain (if it ever worked in the first place; but never underestimate the ingenuity of an old programmer who's slowly losing the race to technology).
  • No way to clearly document (or even explain) how the system works. Class models make absolutely no sense.
  • No hope of ever obtaining software reuse.
  • Frustration and hopelessness on the part of testers.

How may I am figure out if I am using Functional Decomposition Anti-Pattern instead of Command Pattern?

  • 2
    Are you sure that the Command Pattern is essential to Clean Architecture? The architecture page you linked doesn't even mention the Command Pattern. – Robert Harvey Jan 4 '17 at 16:35
  • 7
    To describe functional decomposition as an anti-pattern is pure folly. – David Arno Jan 4 '17 at 16:36
  • Yeah, I think you're laboring under an invalid assumption. I've tried several Google searches for Clean Architecture, Use Cases and Interactors, and haven't found a single mention of the Command Pattern or Execute() methods. – Robert Harvey Jan 4 '17 at 16:38
  • 2
    Beware, Alexander Shvets (the author of that book) totally misused the term "Functional decomposition", please do not support this, it leads only to confusion. Though the anti-pattern surely exists, using an existing term for it which had a completely different meaning was a really, really bad idea. This SO questions gives an explanation how to use the term correctly. – Doc Brown Jan 4 '17 at 18:25
  • 1
    The point seems fair. Encapsulating methods in individual classes indiscriminately seems rather useless, whereas the Command Pattern actually fulfills a specific purpose. See my answer below. – Robert Harvey Jan 4 '17 at 19:33
8

I think the major problem here is Shvets's anti-pattern description is flawed, that is why you have problems to distinguish it from the Command pattern:

  1. The name is flawed because it uses an older, existing term "Functional Decomposition" together with the attribute "anti-pattern".

    This term was AFAIK not associated with the notion of a "pattern" or "anti-pattern" before, but by picking it for his topic Shvets gave it a new, confusing meaning. The original meaning in programming is "taking a complex process and breaking it down into simpler parts", and that is how I would use the term still today. (Note the Wikipedia definition is about the term more generally in mathematics, but I think this is probably not really helpful in our programming context.) See also this former SE post, where other people already had problems with the anti-pattern name, the top answer suggests the name "procedural decomposition" instead, but that name might lead to similar problems.

  2. The first three symptoms you cited from the pattern description are indeed a good fit for command objects, and sometimes also for other, well desgined classes. That is exactly the reason why they are IMHO not a good indicator for this kind of "degenerated architecture" Shvets describes. See, for example, this former SE post about "classes with a single action as function"

What Shvets probably had in mind was a situation where people try to shoehorn procedural code into classes. This happens most often on a completely different scale (he mentioned "Application Scale") than the scale of a typical command object, which will often be just a small class. It results in code which is not SOLID, containing too large classes, missing proper abstractions, and classes where in most cases everything could be static, since the instantiated "object" is not an abstraction for anything else but a "module" or "sub-program", existing once and with the same lifetime as the whole application process.

Command objects, however, are more an object-oriented workaround for situations where the programming language does not provide an inbuilt mechanism for higher order functions or closures. Rember the term "Command Pattern" has its roots in the GOF's "Design Patterns" book from 1995, a time when C++ and Java were dominant, and both of these languages did not provide such mechanisms at that time. So whenever you need to pass a function around in your program as an abstraction of its own, best choice was probably to use the Command Pattern.

Today, both of these languages (as well as other modern languages) provide additional mechanisms for encapsulating functions in a data type, so often you do not even need to use the Command Pattern (in its original OO form) any more. It still can be useful if your "commands" need a more complex interface than just for the "action" or "function" which they encapsulate.

3

The way you tell the difference is that the Functional Decomposition Anti-Pattern guy is using it indiscriminately, whereas the Clean Architecture guy is using it where it actually performs a useful purpose:

The command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.

The Functional Decomposition guy has scrapped all of the benefits of objects in favor of calling functions in class containers that serve no purpose other than to contain a function.

Take a look at the IComparable Interface, which also has a single method.

  • thx. I improved the question description adding the author definition, which lead me to this confusion. – JP Ventura Jan 4 '17 at 19:58
3

Proper use of the command pattern has a difference in time or place between when a command is chosen and when it is executed. It's put in a queue, a callback, an undo stack, etc.

The functional decomposition anti-pattern is just putting methods into classes because you basically have no other choice. You are forced to by the language or by code reviews, but you want to minimize the time and place separation between choosing and executing the command. If you didn't need a box to put the functions in, you wouldn't use classes at all.

  • You make a very positive image of the devs who create software which contains such an anti-pattern ("having no other choice"). To my experience, in most cases where people build ugly code and designs, the devs involved would have had another choice. However, they are often just not experienced enough with OO, uneducated, mismanaged, or simply don't care, especially under time pressure. – Doc Brown Jan 5 '17 at 9:09
  • The positivity was unintentional. I'll see if I can figure out how to reword it. – Karl Bielefeldt Jan 5 '17 at 14:10
  • Don't get me wrong, the fact you have such a positive image about other devs in mind gives me a positive image of yours ;-) – Doc Brown Jan 5 '17 at 14:15
  • I meant the wording was unintentionally positive, but thanks for the compliment. – Karl Bielefeldt Jan 5 '17 at 14:21
2

The Command pattern encodes something that neither Shvets's Functional Decomposition Anti Pattern (FDAP) nor his proposed better way to FDAP have - abstraction.

Command is a means to reason about not just one specific action like CalculateInterest or DisplayTable, but about actions in general, about the abstraction of an action. It's reification at play - the action becomes an object that you can not only run, but also pass along the execution stack until it makes sense to run it, can store somewhere, surround with meta-actions (undo, redo, trace), etc.

In addition to not being prone to a lot of FDAP symptoms (no leverage of inheritance/polymorphism, no reuse), Command trumps it anyway because it's at a whole other abstraction level than the kind of classes that are typically targeted by that antipattern.

  • Sure. Answer updated. – guillaume31 Jan 5 '17 at 14:58
1

Be aware that there is no objective distinction between a pattern and an antipattern. An antipattern is just a pattern applied in a context where it doesn't improve the code.

"Functional Decomposition" describes an architecture which is designed as a series of function calls rather than as collaborating objects. This could lead to Command-classes which encapsulate a single function (because they are thought as function rather than objects and just wrapped in classes without proper consideration on cohesion and coupling) or it could lead to God-classes with a million functions without proper separation of concerns.

If you use the Command pattern in a context where you don't actually need the Command pattern, you might be falling into the function decomposition antipattern.

(Note also that antipattern does not mean bad code. It just means bad object oriented design. Functional languages show that functional decomposition is a very powerful paradigm. It is just not object oriented design.)

  • I would argue that the distinction between a good "pattern" vs a bad "anti-pattern" to a large extent reflects bias due to a lack of exposure to various languages. – Frank Hileman Jan 4 '17 at 17:06
  • @FrankHileman: A pattern is neither good or bad in itself, it all depends on context. An antipattern is by definition bad though, since they are patterns of bad design. – JacquesB Jan 4 '17 at 17:20
  • 1
    The fact "Functional Decomposition" can be sometimes good and sometimes bad has IMHO caused by the fact the term has different meanings. It depends on if you are using it in the way most educated programmers use it, or in the "fancy way" Alexander Shvets (ab)uses it. – Doc Brown Jan 4 '17 at 18:33
  • @DocBrown: Yeah, it is a confusing name for the antipattern. It should be called something like "procedural thinking". – JacquesB Jan 5 '17 at 15:46
1

Metaphors are inherently leaky abstractions, but my thinking style is inherently metaphor-driven, so here is my approach to this question.


Metaphor 1 - the contact persons (or "project leads") of each department in a multi-department organization.

Description

  • To get a particular task accomplished in a department, tell a particular contact person about the particular task, and let that person organize the effort inside that department.

Analysis

  • Command pattern encapsulates "telling that contact person".

Metaphor 2 - The famous "input, process, output" black box.

Description

  • The initiator provides all of the inputs needed by the process.
  • The initiator lets the process start.
  • While the process is executing, there is no opportunity for interaction or intervention between the initiator and the executing process.
  • When the process finishes, the initiator either gets the output, or sees a failure.

Analysis

  • In this analogy, it is a borderline choice between object orientation versus procedural programming.
  • If the inputs, the process itself, and the outputs are all simple, it is worthwhile to consider downgrading it from an object into a function.
  • However, if any of the three things (inputs, the process, and outputs) are non-trivial, as in:
    (i) Having a large numbers of them, or
    (ii) A process that involves many internal states or many internal steps),
    then it is still worthwhile to promote this process from a function into an object.
  • In this case, the unit of reuse is the process itself.

(WARNING: animal cruelty alert)


Metaphor 3 - Ikizukuri

Description

  • An organism is sliced into thin pieces, while still alive.
  • The organism's imminent death is inevitable.
  • The process of slicing a living organism into pieces causes destruction to many vital organs and tissues.
  • Consider the blood vessels and capillaries. They carry nutrients (inputs) and waste (outputs) around the living organism. The destruction of blood vessels means the organism's life cannot be sustained.
  • Individual internal organs are also sliced into parts.
  • The internal structures inside each organ are also sliced into parts.

Analysis

  • A functionality that internally requires a lot of states, inputs and outputs are dismembered into several parts.
  • These parts have no possibility of reuse in isolation.
  • In order to fulfill the functionality, the states, inputs, and outputs will need to be flowing somehow between these parts.
  • Often, this information flow happens as global states (more specifically "globally shared mutable states"), in the context of procedural programming.

Metaphor 4 - A living organism, having its organs intact

Description

  • This living organism is spared the fate of being sliced alive.
  • The internal organs in this living organism are connected by a vast network of circulatory systems, nervous systems, etc.

Analysis

  • When the artifacts of the functional decomposition anti-pattern are properly encapsulated, it is not considered an anti-pattern anymore.
  • Often, this encapsulation process results in the parts being re-integrated into objects, using object-oriented design.
  • Encapsulation (escape from a software project's death) is no longer a viable option when the organism (the software system in question) is already dead for some time.

(Disclaimer: no ikizukuri occurs in the process of making this list of metaphors.)

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.