Chapter 1. C# Meets Design Patterns
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.
An important part of each pattern’s description is a Unified Modeling Language (UML) class diagram. UML is a universally accepted way of describing software in diagrammatic form. The diagrams in the book make use of the UML features itemized in Table 1-1.
Types and parameters
specified when important; access indicated by
Name starts with
Any descriptive text.
Grouping of classes and interfaces.
B inherits from A.
B implements A.
A and B call and access each other’s elements.
Association (one way)
A can call and access B’s elements, but not vice versa.
A has a B, and B can outlive A.
A has a B, and B depends on A.
There are three kinds of blocks, for classes, interfaces/abstract classes, and packages. The class is the most common diagram element and contains details of some of its corresponding C# class’s more important attributes (or fields) and operations (or methods). A UML diagram is not meant to be an exact copy of a program, and thus only the elements that are important to the pattern under consideration are shown. The accessibility of all attributes and operations (private, public, or protected) is indicated. The default for attributes is private and for operations is public. Deviations from the defaults will be highlighted as they occur.
The types associated with attributes and operations are not usually given. However, when these are important, they can be inserted after the identifier, separated by a colon. The same relaxed approach applies to parameters of methods, which are not usually shown in a diagram.
Notes are very useful for explaining relationships, such as when a method in one class calls a particular method in another, when this information is directly relevant to the pattern. In most cases, though, six types of lines give enough information. The Decorator pattern, which we will consider first, has a reasonably rich diagram, and it will be used to explain the lines in more detail.
About C# 3.0
C# 1.0 came out in December 2002, embodying much of the research in OOP that had taken place since Java was launched seven years previously. C# 2.0 was released in final form in September 2005, and the ECMA standard was made available in June 2006. C# 2.0 added five significant features to C# 1.0, most of which are used in the patterns in this book:
Generics that allow classes, structs, interfaces, delegates, and methods to be parameterized by the types of data they store and manipulate
Anonymous methods that allow code blocks to be written “inline” where delegate values are expected
Iterators, which are methods to incrementally compute and yield sequences of values
Partial types that allow classes, structs, and interfaces to be broken into multiple pieces stored in different source files for easier development and maintenance
Nullable types that represent values that possibly are unknown; they support all possible values of an underlying type plus an additional null state
Within Microsoft, work continued on the language, with a particular emphasis on the integration of SQL database interfacing and the associated dynamic typing required. The report on Version 3.0 of the language, finalized in May 2006, includes substantial advances in integrating the functional and database programming paradigms into mainline object-orientation:
Implicit typing of local variables, which permits the types of local variables to be inferred from the expressions used to initialize them
Extension methods, which make it possible to extend existing types and constructed types with additional methods, outside their definitions
Object initializers, which ease construction and initialization of objects
Implicit typing of arrays, which is a form of array creation and initialization together where the element type of the array is inferred from the initializer
Query expressions, which provide a language-integrated syntax for queries that is similar to relational and hierarchical query languages, such as SQL and XQuery
All these new features are used in a natural way in this book. For full details on the entire C# 3.0 language, see the reference documentation available at http://msdn2.microsoft.com/vcsharp.
About the Examples
They are programmable in no more than 180 lines, and usually 100.
They are related to real computer systems.
They are extensible with more functionality.
You’ll find programs related to real-world systems, such as Flickr and Facebook, as well as chat rooms, games, and blogs. We’ll also tackle topics such as embedded systems and manufacturing. Some traditional examples (to do with banking and student marks, for instance) are included as well, but for the most part, the examples are new and have not been seen before in books of this nature. A key factor is that, thanks to the incorporation of design patterns and the deft use of C# 3.0, the programs are remarkably short. Examples that would not have been feasible to show in full in a book before are now perfectly reasonable.