FOREWORD
When you’re faced with a problem to solve (and
frankly, who isn’t these days?), the basic strategy usually taken by we
computer people is called “divide and conquer.”It goes like this:
- Conceptualize the specific problem as a set of smaller
sub-problems.
- Solve each smaller problem.
- Combine the results into a solution of the specific problem.
Reducing complex problems down to the level of twiddling the states of
a few billion bits is what we do all day. But “divide and conquer”is
not the only possible strategy. We can also take a more generalist
approach:
- Conceptualize the specific problem as a special case of a more
general problem.
- Somehow solve the general problem.
- Adapt the solution of the general problem to the specific problem.
Design patterns are among the major tools in the toolboxes of those who
espouse the generalist approach. If you look at samples from a broad
spectrum of software solutions, you will find that though the specifics
may vary widely, there is often an underlying structural similarity.
(Searching a filesystem for a file with a particular attribute is in
some sense structurally similar to searching an annotated parse tree
for a symbol with a particular type.) Design
patterns codify general
solutions to common problems.
The ultimate example of the generalist
approach is of course the design and implementation of programming
languages themselves. As problem solving tools go, it is hard to get
more general than a programming language like C#. When
designing new programming languages (or new versions of old programming
languages), we think about common problems that are faced every day by
real developers
and figure out how to create a language which solves them in a general,
aesthetically pleasing, and powerful way that is broadly applicable.
We want to embed the most useful and powerful abstractions so deeply
into the language infrastructure that you barely even consciously
register them as being
there anymore. Patterns like “local variable”or “procedure call”or
“while
loop”are so much a part of the air we all breathe that we don’t even
think of them
as patterns anymore.
Furthermore, we want to make a language in which patterns which are
useful but perhaps not quite so fundamental are nevertheless relatively
straightforward to
implement clearly and elegantly. A class in C# may be marked as
“static,”or
“abstract,”or “sealed,”but not as “singleton.”That was a deliberate
choice of the
language designers. However, implementing a singleton class in C# is
still relatively easy.
The gray zone in between “clearly foundational”and “occasionally
useful”is where the interesting design challenges lie. Our observations
of design
patterns used by real-world developers in C# (and other languages)
strongly drive the
design process for new versions.
Consider for example how you would implement an iterator pattern on a
linked list in C# 1.0. You would end up defining an enumerator class to
represent a
position in the list containing a lot of boring boilerplate code (which
impedes
readability), and the solution would not be very reusable. The notion
of “enumerate a set
of things”is sufficiently applicable to a wide variety of problems that
it met the
bar for inclusion as a first class language concept. In C# 2.0 with its
yield return
statement the compiler can generate all the boring code for you, and
the generic type system
makes iterating over a set of things typesafe no matter what the
“things” are.
All of this is a long way to say just why it is that I am so very
excited about Language Integrated Query (LINQ) in C# 3.0. We believed
that iterating over
collections of things was a great start, but that we could do so much
more. Sorting,
filtering, grouping, joining, projecting, and transforming data are
also fundamental
operations that are useful in pretty much every domain. Whether you are
writing a ray
tracer, a compiler, an XML reader, or an online banking security
system, odds are good that
you are going to need to manipulate collections of something in a rich
way.
By moving these concepts out of domain-specific object models and into
a generalpurpose programming language, we hopefully solve those more
general problems. We additionally hope, though, that by adding C# 3.0’s
query
expressions, lambda expressions, extension methods, initializer
expressions, expression
trees, and so on to the already rich set of C# 2.0 and 1.0 features, we
make it easier
to elegantly implement all sorts of other useful design patterns.
And that is also why I am excited about this book. C# 3.0 Design
Patterns brings the frequently abstruse world of design patterns into
sharp focus with
pragmatic C# 3.0 implementations. I look forward to seeing where
developers can go with
these tools and this language, and what useful patterns we can build
into the
infrastructures of future languages.
—Eric Lippert
Senior Developer
C# Compiler Team
Seattle, Washington
November 30, 2007
Erik Meijer Technical Lead Microsoft WebData Team
31 July 2003