Single Choice Principle

The exhaustive list of alternatives should live in exactly one place.

Object oriented systems handle this by having a class hierarchy. For example old style procedural code to print different "shape" objects might read:

 if (type==CIRCLE)
   print "Circle.  r=" + radius;
 else if (type==Square)
   print "Square.  sides=" + sideLength;
 else if (type==Rectangle)
   print "Rect.  h=" + height + " w=" + width;

But then this same list of alternatives would be used in computing the area, and it rendering to a GUI and in checking for overlap with another shape, etc., etc.

So rather than have lists of cases which have to be modified in all locations whenever you add a new shape, use subclasses. Notice this supports the OpenClosedPrinciple also, because it allows you to add a new case (keeping the system "open") without modifying the superclass (which is "closed").

[Actually, the procedural code is often quite a bit messier than shown with large chunks of code between each case.]

But I consider method names a "list" also. Is duplicating the "list" of "subtypes" somehow more sinful than duplicating a list of subtypes (methods)? Besides, I find that subtypes often de-generate over time in practice as multiple intersecting factors come (and go) into play. (This may not happen with shapes since the laws of geometry don't change very often, but other domains do change often.) IOW, global taxonomies are a risky structure to bet future maintenance on (AllAbstractionsLie).

Structurally, it is a matter of inverting a nested relationship:

  Variation 1
  --operation A
  --operation B
  Variation 2
  --operation A
  --operation B


  Operation A
  --variation 1
  --variation 2
  Operation B
  --variation 1
  --variation 2

One nesting packing is not necessarily inheritantly better than the other, although I personally find operations more stable over the long run than mutually-exclusive taxonomy "slots". Others have disagreed, but that is my observation of change.

The solution is *not* to put the whole operation into the subclasses, but only those parts which differ between variations. Those common to all variations (for example, the print format or output target) should still reside in exactly one place.

You mean if the implementation is the same. Corresponding equivalents exist for non-sub-typing approaches, I would note. See

Yes, of course. And you could have written something equivalent to Listing 1 using OO techniques.

Besides, Meyer strongly implies that the purpose is to reduce duplication so that you have only one place that needs changing per change request. However, he fails to apply it from a "verb" viewpoint and only shows the noun viewpoint.

I don't understand why you differentiate between those "viewpoints". You could have applied the same techniques to the "verbs". You could even have done both.

The goal is to put things which change for the same reason into the same place.

Things change for a variety of non-anticipate-able reasons.

And still, having to apply a change to only one place in your code is a very desirable property of a design.

{Agreed, but often it is diffult because of the intersection of 2 or more "dimensions". His solution seems to favor changes in one dimension over the other. It is too narrow a treatment of change analysis in my opinion.}

Besides, grouping by "reason for change" is kind of arbitrary if you ask me. I can think of a lot of other "sorts" that would be more practical in a general sense.

There are certainly other forces competing against the SingleChoicePrinciple. What are you thinking of?

Most of the grouping decisions are domain-specific. But, I tend to group code by task.

Yes, I did understand that. My question still is: What forces drive you to do so?

I suppose because behavior is the weakest grouping ability of a database-centric approach. It is easier to farm off some or most of the grouping of other factors to the database. That makes the grouping virtual, not physical like it would be in code. Virtual means you can customize your view of it. See CodeAvoidance. Describing and classifying nouns is best done in the database such that the code's concern is mostly processes. If you want to better manage noun classification, then the database is a better tool for such, largely because it can better provide multiple virtual classifications on the same given noun. Textual code is limiting in this regard. A given car part can both be classified from an engineering perspective AND an accounting perspective, for example. I don't have to choose one over the other. OOP cannot do this without either making a convoluted mess or reinventing a database in code. -- top

Critical comment about Meyer's chapter on the topic:

True, it often comes down to inverting the nested relationship and choosing one to be in one place while the other is required for all subclasses of a class. However, that does not invalidate the principle -- it just points out that you sometimes hit a case (nested relationship) where you can't apply SingleChoicePrinciple everywhere and you have to choose one place.

Certainly we all agree that scattering case statements all over is a pain to deal with -- the cases in the various statements diverge and you can't tell what *exactly* happens where after a while.

Good programmers would agree with that statement.

Also note that VisitorPattern is a technique for inverting the nested relationship if you feel that you have the wrong one embodying this priciple. (In visitor, the method is one class and has an entry for every subtype so you can add a new method by adding a single encapsulated class.)

Ranty HolyWar stuff:

Re: By topmind, a controversial programmer who prefers procedural techniques, so of course he'd say that. It certainly isn't a viewpoint many share, and shouldn't be presented as such.

RE: It certainly isn't a viewpoint many share, and shouldn't be presented as such.

Even many OO proponents agree that polymorphism and sub-typing are overused. They simply point to other attributes of OO as the key selling point of it. See: OoBestFeaturePoll.

Mmm...not sure "overused" is quite the right word. Sometimes misused, and sometimes oversold, perhaps is closer.

Should this all somehow be merged with SwitchStatementsSmell? It is true that Single Choice is more general than switch/case statement issues, but the examples usually end up pointing to switch/case statements (and their if/else cousins).
Isn't this covered by the Class FactoryPattern?

See SwitchStatementsSmell, LimitsOfHierarchies, DeltaIsolation

View edit of October 18, 2012 or FindPage with title or text search