[25.6] Is there a simple way to visualize all these tradeoffs?
Here are some of the "goodness criteria," that is, qualities you might want.
In this description, N is the number of geographies and M is
the number of power sources:
- Grow Gracefully: Does the size of the code-base grow
gracefully when you add a new geography or power source? If you add a new
geography (going from N to N+1), do you need to add one new
chunk of code (best), M new chunks of code (worst), or something in
between?
- Low Code Bulk: Is there a reasonably small amount of code
bulk? This usually is proportional to ongoing maintenance cost — the more
code the more cost, all other things being equal. It is also usually related
to the "Grow Gracefully" criteria: in addition to the code bulk of the
framework proper, best case there would be N+M chunks of code, worst
case there would be N*M chunks of code.
- Fine Grained Control: Do you have fine granular control over
the algorithms and data structures? For example, do you have the
option of having a different algorithm and/or data structure for any
of the N*M possibilities, or are you stuck with using the same
algorithm and/or data structure for all, say, gas powered vehicles?
- Static Detect Bad Combos: Can you statically ("at compile
time") detect and prevent invalid combinations. For example, suppose for the
moment that there are no pedal-powered space vehicles. If someone tries to
create a pedal-powered space vehicle, can that be detected at compile time
(good), or do we need to detect it at run-time?
- Polymorphic on Both Sides: Does it let users treat either base
class polymorphically? In other words, can you create some user code
f() that takes any and all, say, land vehicles (where you can add a
new kind of land vehicle without requiring any changes to f()), and
also create some other user code g() that takes any and all, say, gas
powered vehicles (where you can add a new kind of gas powered vehicle without
requiring any changes to g())?
- Share Common Code: Does it let new combinations share common
code from either side? For example, when you create a new kind of gas
powered land vehicle, can that new class choose to optionally share code that
is common to many gas-powered vehicles and choose to optionally share
code is common to many land vehicles?
This matrix shows techologies as rows and "goodness criteria" as columns.
means the row's technology has the column's goodness
criteria, "no" means it does not.
| Grow Gracefully?
| Low Code Bulk?
| Fine Grained Control?
| Static Detect Bad Combos?
| Polymorphic on Both Sides?
| Share Common Code?
|
Bridge
|
| 
(N+M chunks)
| no
| no
| no
|
|
Nested generalization
| no
| no
(N*M chunks)
|
|
| no
| no
|
Multiple inheritance
| no
| no
(N*M chunks)
|
|
|
|
|
Important: do not be naive. Do not simply add up the number of
s, choosing based on most good or least bad. THINK!!
- The first step is to think about whether your particular situation
has other design options, that is, additional rows.
- Recall that the "bridge" row is really a
pair of rows — it has an assymetry that could
go in either direction. In other words, one could put an Engine* in
Vehicle or a Vehicle* in Engine (or both, or some
other way to pair them up, such as a small object that contains just a
Vehicle* and an Engine*).
- Similar comments for the nested generalization row:
it is actually a pair of rows because it also has an assymetry, and that
assymetry gives you an extra option: you could first decompose by geography
(land, water, etc.) or first by power source (gas, nuclear, etc.). These two
orders yield two distinct designs with distinct tradeoffs.
- The second step in using the above matrix is to think about which
column is most important for your particular situation. This will let
you give a "weight" or "importance" to each column.
- For example, in your particular situation, the amount of code that
must get written (second column) may be more or less important than the fine
grained control over algorithms/data structures. Do not get caught up
trying to figure out which column is more important in some abstract, generic,
one-size-fits-all view of the world, because one size does not fit all!!
- Question: is code bulk (and therefore maintenance cost) more or less
important than fine grained control? Answer: Yes, code bulk (and therefore
maintenance cost) is either more or less important than fine grained control.
That's a joke; lighten up.
- But this part isn't a joke: don't trust anyone who thinks they know
whether code bulk (and therefore maintenance cost) is always more or always
less important than fine grained control. There's no way to know until you
look at all the requirements and constraints on your particular
situation! Far too many programmers think they know the answer before
they are familiar with the situation. That's worse than dumb; it is
unprofessional and dangerous. Their one-size-fits-all answer will
sometimes be right. Their one-size-fits-all answer might have been
right in every case they have ever seen in their limited range of experience.
But if their past success blinds them from asking the tough questions in the
future, they are a danger to your project and should get a thonk on the noggin
("thonk" and "noggin" are highly technical terms).
Your ultimate choice will be made by finding out which approach is best
for your situation. One size does not fit all — do not
expect the answer in one project to be the same as the answer in another
project. Your past successes can become, if you are not careful, the seeds of
your future failure. Just because "it" was best on your previous project does
not mean "it" will be best on your next project.