Covering real life in a program shouldn't make it uglier than a
program covering only textbook cases. To handle the more numerous and
complex cases, you will _generalize_ the code. This will let you
avoid handling specific cases in specialized code, but instead cleanly
handle all the cases with a single rule.
That's the same principle in physics (and really in all sciences),
where from a lot of seemingly disparate and noisy experimental data,
you elaborate a general theory, with a model that's beautiful and
simple (ie. which reduces to a few simple and nice equations).
Some design patterns are useful to perform this abstraction, like the
interpreter design pattern or the emacs design pattern.
On the other hand, it means that most of the code you will write or
use, won't deal with the specific problem, but with more abstract
considerations (such as how to manage resources, or how to transform
code), like in physics, most of the theory don't deal with actual
physical phenomenon, but are actually mathematical theories that seem
quite remote.
Let's take a few examples.
Often, users specify more cases than needed. For example, they may
say that some price depends on some parameter, such as the number of
persons:
1 -> $10 2^0
2 -> $20 2^1
3 -> $40 2^2
4 -> $80 2^3
It is obvious that there's actually a formula:
price = 10*2^(nperson-1)).
Actually, there's always a simple formula, since any set of N points
can be extrapolated by a polynom of degree N+1.
The interpreter pattern let you decompose the operations you have to
perform in the different cases, in a set of simple operations that are
specific to the problem domain. The specific cases can then expressed
as simplier "programs" using those operations. In a way, this allows
to decompose the problem in two orthogonal parts, one set that
contains generic simple operations, and another that contains simplier
programs specific to the concrete cases. Since those real-life cases
often change, having a domain specific language to express them let
also write them more easily and quickly, and even dynamically
(ie. change the specific case programs without changing the software,
just changing the data that is intepreted).
Another pattern found in emacs, and similar to the garbage collector,
is the display engine.
In the case of the garbage collector, we decouple the memory
management from the actual program, by having a separate algorithm,
orthogonal to the problem specific algorithms, to deal with the
problem of memory allocation and release. Once the garbage collector
has all the information it needs to be able to release safely the
memory that is not used anymore, it can do its job without interfering
with the domain specific program.
Similarly, the display engine is entirely decoupled from the rest of
the editor in emacs. The display engines is able to detect by itself
when the contents of the buffers change, and to compute alone the
difference between what is displayed on the screen and what needs to
be displayed after the changes. It can then produce an optimized
update sequence for the screen or terminal. The rest of the emacs
editor routines can modify the buffers with absolutely no
consideration for the displaying, which simplifies greatly their code.
In conclusion, if write your program as some general rule performing
the same treatment to all cases, and encode the specific real-life
cases as specific data to be processed by the general rule, you can
obtain a program that still handle all the specific cases, but doing
that in the most general way, and therefore being as clean and as well
designed as you wish.