What makes a successful and happy programmer? What contributes to that wonderful "gotcha!" feeling you get when your program finally compiles and runs correctly, and you know that it is as elegant as it can be? How did you manage to make it elegant while at the same time satisfying all the normal "-ilities" (flexibility, maintainability, reliability, and reusability, to name a few)? And why are some programmers able to attain this level of elegance so much quicker than others?
It would be easy to say that some are born to program and some are not. Yet even the best programmers will sit for hours or even days poring over a single screen of code, knowing it is not quite right and struggling to make it better. The answer is that a successful programmer has two primary tools: a good programming language and design patterns. This book is devoted to showing how this winning combination works together to launch ordinary programmers into the realm of experts.
Those who have long-term programming experience will appreciate that time brings improvements to a language. Simple things that we take for granted today—like type checking of variables—were nonexistent or optional in the languages of the 1970s. Object orientation, which is the basis for programming these days, only came into vogue in the 1990s, and generics—on which our modern collection classes for stacks, maps, and lists are based—were just a research project five years ago.
Successful programmers keep abreast of improvements in languages, but often it is not obvious even to a seasoned professional how a particular new feature will be useful. Some features, such as automatic properties (Chapter 3) and collection initializers (Chapter 3), are likely to immediately find a home in your toolbox; others, such as extension methods (Chapter 2), are somewhat more abstract.
Examples are needed to illustrate the utility of many emerging language features—but while examples illustrate, they can also obscure because they are directed toward solving particular problems. Given an example of how iterators work with a family tree manager (Chapter 9), would you be able to reuse them for a chat room program? The connection is not at all obvious and could easily be missed. Enter design patterns, the ultimate in mind connectors for successful programmers.
Design patterns encapsulate common ways of solving problems using language features together.
Design patterns provide a high-level language of discourse for programmers to describe their systems and to discuss solutions to common problems. This language comprises the names of recognizable patterns and their elements. The proper and intelligent use of patterns will guide a developer into designing a system that conforms to well-established prior practices, without stifling innovation. In the marketplace, design patterns greatly enhance practitioners' mobility and the value of their knowledge, as they provide a common, recognizable basis for tackling problems.
The patterns have illustrative names and are described with diagrams illustrating their role players. There are only 23 classic patterns (fewer than the letters of the English alphabet), and a good programmer can learn the names and uses of all of them with some practice. When faced with design choices, such programmers are no longer left to select language features, such as inheritance, interfaces, or delegates. They can instead hone in on the bigger picture: a blog would match an Observer pattern (Chapter 9), a community network system would need a Proxy (Chapter 2), and so on. The element of decision making is not removed, but it is raised to a higher level.
So, who decides how a design pattern is implemented in a given language? Books such as this one and writings on web sites present the implementations of the patterns, together with guidance on how to choose a pattern and even how to select among alternative implementations (if there are any). However, the pull of custom is very strong, and often patterns are presented using only the language features of the 1980s. Not so in this book. C# 3.0 Design Patterns aims to present the 23 classic patterns in the best possible C# 3.0 style, ensuring that what you learn here will be of real value to you for many years to come.
Design patterns were introduced in Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides's seminal work Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley). The book specifies and describes 23 patterns that form the foundation of any study of the subject, which are still regarded as the essential core patterns today.
These core patterns address issues in mainline object-oriented programming (OOP), and the original implementations were presented in C++ and Smalltalk (the primary OOP languages at the time they were developed). Since then, other books have implemented the patterns in Java, Visual Basic, and C#. As the value of the pattern concept has become accepted, new patterns have been proposed to add to the original list. In addition, there are now patterns that are applicable to specific areas, such as software architecture, user interfaces, concurrency, and security. Although these patterns are extremely important in their areas, their adherents are fragmented, and the core set of universally accepted patterns has not been expanded.
As outlined in the Preface, the discussion of each pattern in this book consists of a brief description of its role, an illustration, a look at its design and implementation, and an example, followed by a discussion of its uses and some exercises. New features of the C# language are introduced where patterns draw upon them; thus, you will learn more about the language as you learn about the patterns.
The 23 patterns are divided into three groups: creational, structural, and behavioral. Within a group, though, there is no inherent ordering, and alphabetical ordering has traditionally been the default. In this book, we take an innovative approach by relating the patterns to the language features they require and introducing them in order of increasing language complexity. Several of the patterns in each group need only inheritance or interfaces, and it makes sense to deal with these first so that the focus can be on the patterns themselves and not on the language. The patterns that make use of more advanced language features (generics, indexers, and delegates) are then presented later. Novel features of C# can thus be introduced as we go along, rather than in a standalone introduction or appendix. A comprehensive index complements this approach.
A key feature of any pattern handbook is the insight that it gives as to the use of patterns in real systems. Knowing that the Façade pattern is frequently used in compiler construction, or that adapters are prevalent in well-known graphical frameworks, reinforces their importance and helps to direct their use. However, in large systems patterns are seldom found in isolation, and often they work together. The Singleton pattern, for example, is often used in conjunction with other patterns when it is necessary to create only one version of a component. Thus, considerable attention is given at the end of each chapter to the comparative merits of the patterns explored.