Case Statements Considered Harmful

A belief that all or most case statements should be turned into subclasses. Some suggest that only duplicate case statements indicate the need to subclass. Named as pun on famous 1968 paper by Edsger Dijkstra (re)titled "Go To Statements Considered Harmful" (GotoConsideredHarmful).

The belief is widespread in popular OO literature. It is ironic however that modern FunctionalProgramming (MlLanguage, HaskellLanguage, ObjectiveCaml, CleanLanguage and others) recovered the bad mouthed case and generalized it as PatternMatching which can be viewed as "case on steroids". Sometimes the PatternMatching you see in FP examples is exactly the TypeCase that OO programming holds should be absolutely forbidden. And many FP-ers including the language designers are very proud of their hack.

On the OO front one can see library designers who have chosen to swallow the bitter pill and do essentially a TypeCase because the lack of MultipleDispatch exactly where it is most needed: in frameworks. A typical example is event dispatching which is polymorphic on the type of event as well as on the type of the receptor (and sometimes even a third argument might come into picture). Since typical OO languages support only simple polymorphism on one argument, the choice remains between the harmful case statement or deploying the DoubleDispatch pattern, which is not exactly a nice hack.

So where does this leave CaseStatementsConsideredHarmful?

It leaves it in the hands of the language designers. A SwitchStatement is a great way to implement a lexer, a parser, or some other text-based dispatch. It is a lousy way to fake polymorphism.

Furthermore, some consider the definition of switch/case statements in Java/C++ and similar languages to be broken - error-prone fall-through logic, works only on primitive data types, no ranges, awkward syntax, ...

The awkward way the C family does them is discussed in IsBreakStatementArchaic.

I believe a more general statement is that unnecessary control logic should be avoided. If the code can be restructured to remove or avoid a branch, if, switch, throw, etc., it should. If not, then use the control statement that is the most appropriate.

Something about this issue confuses me. If the Smalltalk "language" has no case statement, then the Smalltalk language has no if statement either. That doesn't seem to worry anyone. Never heard it mentioned, anyway. Meanwhile, the class Object does have methods caseOf: and caseOf:otherwise:

The Smalltalk language itself does not know an if-statement. It's in the library; methods named ifTrue: and ifTrue:ifFalse: (and sometimes heretics like ifFalse:ifTrue:) implemented in the Classes True and False.

Sometimes, a case statement (ignoring the horrible implementation of switch in C/C++) is the right tool for the job. The comments about refactoring case statements into polymorphism apply if the choices are of different types, but if I have to switch on one of several ints... ie

 int i = getSomeValue();

switch (i) { case 1: do_this(); break; case 2: do_that(); break; case 3: do_the_other(); break; default: handle_everything_else(); break; }

No good way of refactoring THAT into a subclass. (Well, maybe in Smalltalk...). Even if I could treat different numerical values as subclasses of some base type; adding methods to the fundamental types of the language seems to me to be an ill-advised thing to do.

Really? What about an object like:

	BetterInt? i = get_some_value()
	i ifIs: 1 then: [do_this()] ifIs: 2 then: [do_that()] Otherwise: [do_something_else()]

...Oh wait, you just said not smalltalk (and by extention, Ruby {which also has Blocks}). Is some sort of int2type wrapper using templates useful? -- BillWeston

Ug. GettersConsideredHarmful?.

Also see PolymorphismVsSelectionIdiom for a solution to the problem given above.
So is DuffsDevice an argument for or against this pattern? DuffsDevice is an argument against CeeLanguage :-)
In JavaLanguage, AbstractMethods? on EnumeratedTypes are a great way to replace case statements ... provided that the enumerated type is not essentially business data that ought not to know about the detail of business code.

For instance, if you have an enum for state (Vic, NSW, Tas, ACT ...) and you have an obscure business rule that depends on whether a state is a state or territory, you would not put that rule in the enum. The better way to go is to put an isState or isTerritory method in the enum, and have the rule depend on that.

I'm not sure what you would call the pattern, but enums are (usually) very basic data. Methods in them should be true about the enum itself, not about what you want to do with the enum. In mangled smalltalk-ese (I am not a ST programmer), java enums are usually messages that you sent to things rather than active objects that take messages and respond to them. I suppose that's obvious from the fact that they are static singletons. - PaulMurray

If there are a lot of territories or if they will change fairly often, I usually put such info in a database, or at least a user-configurable file, and have an attribute that tells whether something is a state or a region (location category). See DispatchOnFeaturesNotClassifications.

True, but the addition of a new State (as in "state of the commonwealth") is such an enormous change that it's not unreasonable that the code needs a recompile. After all, we are talking here about the kind of code that one would be tempted to put in a switch statement.

An interesting blog entry on a given conditional metric:

See Also: HugeCaseStatements, SwitchStatementsSmell, IsBreakStatementArchaic

View edit of January 8, 2013 or FindPage with title or text search