Buy this Book
Print Book $39.99 PDF $27.99 Read it Now! Print Book £24.99
Add to UK Cart
Reprint Licensing

C# 3.0 Design Patterns
C# 3.0 Design Patterns

By Judith Bishop
Book Price: $39.99 USD
£24.99 GBP
PDF Price: $27.99

Cover | Table of Contents | Colophon


Table of Contents

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 () and collection initializers (), are likely to immediately find a home in your toolbox; others, such as extension methods (), 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 (), 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
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
About Patterns
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.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
About UML
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 : UML class diagram notation
Program element
Diagram element
Meaning
Class
Types and parameters specified when important; access indicated by + (public), (private), and # (protected).
Interface
Name starts with I. Also used for abstract classes.
Note
Any descriptive text.
Package
Grouping of classes and interfaces.
Inheritance
B inherits from A.
Realization
B implements A.
Association
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.
Aggregation
A has a B, and B can outlive A.
Composition
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.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
About C# 3.0
An objective of this book is to present C# in its very best style. The version we are using is C# 3.0 on the .NET 3.5 Beta 2 Framework (August 2007).
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
  • Lambda expressions, an evolution of anonymous methods that provide improved type inference and conversions to both delegate types and expression trees
  • Object initializers, which ease construction and initialization of objects
  • Anonymous types, which are tuple types automatically inferred and created from object initializers
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
About the Examples
The nearly 40 full example programs in this book have been carefully crafted to satisfy the following criteria:
  • 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.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Structural Patterns: Decorator, Proxy, and Bridge
We start our tour of design patterns with the group known as the structural patterns. There are seven patterns that make up the structural group, each with the role of building flexibility, longevity, and security into computer software. The names of the patterns are important, so I'll introduce them immediately. They are:
  • Decorator
  • Proxy
  • Bridge
  • Composite
  • Flyweight
  • Adapter
  • Façade
Structural patterns are concerned with how classes and objects are composed to form larger structures. Of the many purposes of the seven structural patterns, here are 10:
  • Add new functionality dynamically to existing objects, or remove it (Decorator).
  • Control access to an object (Proxy).
  • Create expensive objects on demand (Proxy).
  • Enable development of the interface and implementation of a component to proceed independently (Bridge).
  • Match otherwise incompatible interfaces (Adapter).
  • Reduce the cost of working with large numbers of very small objects (Flyweight).
  • Reorganize a system with many subsystems into identifiable layers with single entry points (Façade).
  • Select or switch implementations at runtime (Bridge).
  • Simplify the interface to a complex subsystem (Façade).
  • Treat single objects and composite objects in the same way (Composite).
Structural patterns can be employed while a system is being designed, or later on during maintenance and extension. In fact, some of them are specifically useful in the post-production stages of the lifecycle of a software system, when changes are introduced that were not foreseen and when even the interfaces between components need updating. Thus, sometimes when you want to add functionality, you will be working with existing classes that cannot be changed. The Decorator pattern is useful here. Alternatively, you might be able to design a whole system from scratch so that it works in a particularly usable way, with the Composite pattern.
Our exploration of the structural design patterns will span three chapters. In this chapter, we'll look at the Decorator, Proxy, and Bridge patterns. In , we'll tackle the Composite and Flyweight patterns, and in , we'll examine the Adapter and Façade patterns.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Decorator Pattern
The role of the Decorator pattern is to provide a way of attaching new state and behavior to an object dynamically. The object does not know it is being "decorated," which makes this a useful pattern for evolving systems. A key implementation point in the Decorator pattern is that decorators both inherit the original class and contain an instantiation of it.
As its name suggests, the Decorator pattern takes an existing object and adds to it. As an example, consider a photo that is displayed on a screen. There are many ways to add to the photo, such as putting a border around it or specifying tags related to the content. Such additions can be displayed on top of the photo, as shown in .
Figure : Decorator pattern illustration—(a) plain photograph and (b) photograph with tags
The combination of the original photo and some new content forms a new object. In the second image shown in , there are four objects: the original photo as shown to the left, the object that provides a border, and two tag objects with different data associated with them. Each of them is a Decorator object. Given that the number of ways of decorating photos is endless, we can have many such new objects. The beauty of this pattern is that:
  • The original object is unaware of any decorations.
  • There is no one big feature-laden class with all the options in it.
  • The decorations are independent of each other.
  • The decorations can be composed together in a mix-and-match fashion.
Now, we can specify the players in the Decorator pattern in a UML diagram, shown in . Because this is the first pattern we are describing in UML, we'll take it slowly. (The UML that we need for patterns in this book is covered in and summarized in .) The essential players in this UML diagram are:
Component
An original class of objects that can have operations added or modified (there may be more than one such class)
Operation
An operation in IComponent objects that can be replaced (there may be several operations)
IComponent
The interface that identifies the classes of objects that can be decorated (Component is one of these)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Proxy Pattern
The Proxy pattern supports objects that control the creation of and access to other objects. The proxy is often a small (public) object that stands in for a more complex (private) object that is activated once certain circumstances are clear.
A major phenomenon in the world today is the rise in popularity of community networking systems, the most prominent of which is Facebook. A session from Facebook is shown in .
Figure : Proxy pattern illustration—a page in a social networking system
A feature of such systems is that many people who sign up do so only to view what others are up to and do not actively participate or add content. It might therefore be a good policy to start every newcomer with a plain page and not grant users any actual storage space or access to any facilities until they start adding friends and messages.
Another feature of these systems is that you have to first register with the system and then log on at each session. Once logged on, you have access to your friends' pages. All the actions you can perform (poke, write on walls, send gifts, etc.) originate at your browser and are sent across the network to the nearest Facebook server. The objects are the Facebook pages; the proxies are the mechanisms that allow registration and login.
The design of a proxy and its players is shown in the UML diagram in .
Figure : Proxy pattern UML diagram
The players in the pattern are:
ISubject
A common interface for subjects and proxies that enables them to be used interchangeably
Subject
The class that a proxy represents
Proxy
A class that creates, controls, enhances, and authenticates access to a Subject
Request
An operation on the Subject that is routed via a proxy
The central class, Proxy, implements the ISubject interface. The Client can use ISubject to abstract away from proxies. However, as we shall see in the upcoming SpaceBook example, sometimes we can dispense with this interface. Each Proxy object maintains a reference to a Subject, which is where the action really takes place. The Proxy performs frontend work. Its value can be enhanced by making the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Bridge Pattern
The Bridge pattern decouples an abstraction from its implementation, enabling them to vary independently. The Bridge pattern is useful when a new version of software is brought out that will replace an existing version, but the older version must still run for its existing client base. The client code will not have to change, as it is conforming to a given abstraction, but the client will need to indicate which version it wants to use.
Consider the rolling out of a new version of the .NET Framework used to compile and run C# 3.0. You can have several versions of the Framework loaded on your computer at any time and can select which one to use by externally setting a path to it in the Windows operating system. Setting the path is the bridge between what applications want from the Framework and the actual version they will get. In , you can see that there are five versions of the Framework loaded on my computer. shows that the one the system is being directed to is Version 3.5, which includes the C# 3.0 compiler.
Figure : Bridge pattern illustration (a)—five versions of the .NET Framework loaded
Figure : Bridge pattern illustration (b)—environment Path variable set to Version 3.5
Inheritance is a common way to specify different implementations of an abstraction. However, the implementations are then bound tightly to the abstraction, and it is difficult to modify them independently. The Bridge pattern provides an alternative to inheritance when there is more than one version of an abstraction. Consider the UML diagram in . The two implementations, A and B, implement an interface called the Bridge. The Abstraction includes an attribute of type Bridge but is not otherwise in a relationship with the implementations.
Figure : Bridge pattern UML diagram
From this diagram, we can see that the role players for the Bridge pattern are:
Abstraction
The interface that the client sees
Operation
A method that is called by the client
Bridge
An interface defining those parts of the Abstraction that might vary
ImplementationA and
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Example: OpenBook
The Bridge pattern gives us a nice opportunity to extend a previous example and to show different patterns interacting. Let's consider the SpaceBook system again. Suppose a developer is working on a new version of SpaceBook called OpenBook. The main feature of OpenBook is that it does not require password authentication. It also does not bother with lazy instantiation, as it is assumed that all OpenBook users will get onto the system straightaway. OpenBook is thus replacing the proxy level of SpaceBook but not the guts of it, and it is certainly the intention that users of both systems should be able to see each other's pages.
Therefore, we have a design where MyOpenBook and MySpaceBook need to implement a common interface. We shall call this the Bridge interface. The Abstraction is what is commonly called a Portal. It keeps a copy of the appropriate type of book object and routes each of the methods in the interface to the versions in that book object. The main program then reflects these changes:
class BridgePattern : SpaceBookSystem {
  static void Main ( ) {
    MySpaceBook me = new MySpaceBook( );
    me.Add("Hello world");
    me.Add("Today I worked 18 hours");

    Portal tom = new Portal(new MyOpenBook("Tom"));
    tom.Poke("Judith");
    tom.Add("Judith","Poor you");
    tom.Add("Hey, I'm also on OpenBook - it was so easy!");
  }
}
Users like Judith can continue to use the old SpaceBook, as nothing has changed, but Tom can now go in through the Portal (the Abstraction) and link up via the Bridge to the new OpenBook access to SpaceBook. The resulting output starts off as before, but then we see that Tom did not have to register or authenticate:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Pattern Comparison
The careful reader might have noticed that the three patterns described in this chapter seem to offer much the same service. At a high level, they are all helping to extend classes in novel ways, and they all provide alternatives to inheritance. A summary of when each pattern might be used was provided at the end of each section. Because this is also a programming book, we'll now summarize the object mechanisms the patterns use (see ) and draw conclusions about their comparative operation. This summary is based on the UML diagrams and theory code for each of the patterns.
In each pattern, we can identify four roles, which can fall under the headings original, new, interface, and client. The actual names given to these meta-roles are shown in the first three rows of the table. Note that the Bridge pattern can work in two ways. In our examples, we used the "Bridge-up" option. We assumed that we had one implementation already and wanted to share its interface with other implementations yet to be built. To do so, we needed to create an Abstraction that was closely connected to the Bridge interface. An equally valid application for the Bridge pattern would be to have an original abstraction in mind and to build it hand-in-hand with the implementations (the "Bridge-down" approach).
Table : Comparison of Decorator, Proxy, and Bridge patterns
Decorator
Proxy
Bridge-down
Bridge-up
Original
Component
Subject
Abstraction
Implementation
Interface
IComponent
ISubject
Bridge
Bridge
New
Decorator
Proxy
Implementation
Abstraction
Client aggregates
New with interface
New
Original with new
New with original
Client activates
Original and new
New
Original
New
Original changed by
Implementing the interface
No change
Aggregating the interface
Implementing the interface
New classes
Aggregate the interface
Implement the interface
Aggregate the original
Implement the interface
Implement the interface
Aggregate the interface
Operation routed
From new to original
From new to original
From original to new
From new to original
As you can see in the fourth row of the table, variations are found in the clients. For example, the Decorator pattern aggregates the interface so that it can share decorations; it provides the original as a construction parameter. The Bridge-up pattern does the same. To make this more concrete, here are the two statements from the clients:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Structural Patterns: Composite and Flyweight
The Composite and Flyweight structural patterns apply to systems that have many data objects. The Composite pattern has wide applicability, and its composite lists can also make use of the Flyweight pattern. The Flyweight pattern shares identical objects behind the scenes to save space. In their implementations, these patterns make use of the following novel C# features:
  • Generics
  • Properties
  • Structs
  • Indexers
  • Implicit typing
  • Initializers
  • Anonymous types
The Composite pattern arranges structured hierarchies so that single components and groups of components can be treated in the same way. Typical operations on the components include add, remove, display, find, and group.
Computer applications that specialize in grouping data abound these days. Consider a music playlist in iTunes or a digital photo album in Flickr or iPhoto (). Items are put in a large list, which is then given structure separately.
Figure : Composite pattern illustration—iPhoto
Looking at the screenshot from iPhoto, we can see that there are different ways of viewing the photos that have been imported: chronologically or by named event. A single photo can appear in many albums ("Last Roll," "2007," and "Switzerland," for example). Creating an album forms a composite object but does not entail actually copying the photos. In this context, the important point about the Composite pattern is that the operations carried out on photos and albums of photos should have the same names and effects, even though the implementations are different. For example, the user should be able to display a photo or an album (that contains photos). Similarly, deletions of a single photo or an album should behave in the same way.
The Composite pattern is one of the simplest there is, on the face of it. It has to deal with two types: Components and Composites of those components. Both types agree to conform to an interface of common operations. Composite objects consist of Components, and in most cases, operations on a Composite are implemented by calling the equivalent operations for its Component objects. See for the UML diagram for this pattern.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Composite Pattern
The Composite pattern arranges structured hierarchies so that single components and groups of components can be treated in the same way. Typical operations on the components include add, remove, display, find, and group.
Computer applications that specialize in grouping data abound these days. Consider a music playlist in iTunes or a digital photo album in Flickr or iPhoto (). Items are put in a large list, which is then given structure separately.
Figure : Composite pattern illustration—iPhoto
Looking at the screenshot from iPhoto, we can see that there are different ways of viewing the photos that have been imported: chronologically or by named event. A single photo can appear in many albums ("Last Roll," "2007," and "Switzerland," for example). Creating an album forms a composite object but does not entail actually copying the photos. In this context, the important point about the Composite pattern is that the operations carried out on photos and albums of photos should have the same names and effects, even though the implementations are different. For example, the user should be able to display a photo or an album (that contains photos). Similarly, deletions of a single photo or an album should behave in the same way.
The Composite pattern is one of the simplest there is, on the face of it. It has to deal with two types: Components and Composites of those components. Both types agree to conform to an interface of common operations. Composite objects consist of Components, and in most cases, operations on a Composite are implemented by calling the equivalent operations for its Component objects. See for the UML diagram for this pattern.
Figure : Composite pattern UML diagram
The essential players in the Composite pattern UML diagram are:
IComponent
Defines the operations for objects in the composition, and any default behavior that will be applicable across objects of both types
Operation
Performs an operation on IComponent-conforming objects
Component
Implements the operations as applicable to single objects that cannot be further decomposed
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Flyweight Pattern
The Flyweight pattern promotes an efficient way to share common information present in small objects that occur in a system in large numbers. It thus helps reduce storage requirements when many values are duplicated. The Flyweight pattern distinguishes between the intrinsic and extrinsic state of an object. The greatest savings in the Flyweight pattern occur when objects use both kinds of state but:
  • The intrinsic state can be shared on a wide scale, minimizing storage requirements.
  • The extrinsic state can be computed on the fly, trading computation for storage.
Consider now the image aspect of the Photo Library application, as discussed with the Composite pattern. At any one time, we want to have a full page of images displayed and, with no discernable time lag, we want to be able to scroll up and down through the library. This implies that as many images as possible should be preloaded into memory and kept there while the photo application is running. For a Photo Group application, the primary function is to arrange photos in groups. Photos can belong to several different groups, so the number of images to display could increase enormously. If all of the images do not fit in memory, the fact that they can belong to different groups means that any given photo may be called up for display at irregular times, resulting in a lot of disk transfers.
Consider the illustration in . With a smaller window, the first two groups could have scrolled off the window by the time the Food group appears, requiring at least three of the images to be refetched and displayed.
Figure : Flyweight pattern illustration—Photo Group
An object's unshared state is the set of groups to which it belongs. Its extrinsic state is the actual image, which is large—it occupies about 2 MB. However, there are methods in the System.Drawing namespace to convert an image to a thumbnail of about 8 KB. This will be the object's intrinsic state, which is small enough to allow all the unique images to remain in memory at any one time. Through the group information, the application can display the images in various combinations. Using disk fetches, it can also show the complete original-sized images.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Exercises
  1. Implement the final stage of the Photo Group application, which involves fetching and displaying an image (extrinsic state) when its thumbnail (intrinsic state) is clicked.
  2. Integrate the Photo Library and Photo Group applications so that commands can be issued as well as photos displayed. Keep track of the two patterns (Composite and Flyweight) and report at the end of the exercise how they fit together.
  3. In the earlier Composite pattern example (), the specification of the Photo Library was read from a file. As an exercise in object initialization, specify the initial state of the library in the program using the same data:
    AddSet Home
    AddPhoto Dinner.jpg
    AddSet Pets     Going down another level
    AddPhoto Dog.jpg
    AddPhoto Cat.jpg
    Find Album      Ensures Garden is at same level as Home
    AddSet Garden
    AddPhoto Spring.jpg
    AddPhoto Summer.jpg
    AddPhoto Flowers.jpg
    AddPhoto Trees.jpg
    
    (Hint: take these lines out of the data file. The program won't have to change otherwise, as the loop will start processing from the first command it finds in the file, which will be Display.)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Pattern Comparison
I grouped the Composite and Flyweight patterns in this chapter because they both deal with structures of multiple objects. The Composite pattern is concerned with reacting to commands for accessing and altering a data structure in a uniform way, whereas the Flyweight pattern is a clever way of saving space when there are multiple identical objects. They both have the property that the types making up the pattern can be packaged into a namespace and then used by a client.
In our examples, the CompositePattern namespace was programmed generically, giving any client the built-in flexibility of instantiating the pattern to suit its needs. The constraints that I mentioned at the time were that a type used for instantiating Component needed to have Equals and ToString methods defined. The Equals method is essential for the correct working of the Find method, but the ToString requirement can be dispensed with if different output is arranged. The FlyweightPattern namespace, on the other hand, contains the IFlyweight and Flyweight types, which are very specific to the photo application used for illustration. (It also contains the FlyweightFactory class, which is not dependent on any class from the client.)
Flyweight is a server pattern in the sense that it can be useful to many other patterns to keep data compact. Examples we will encounter later are the Interpreter, State, and Strategy patterns. The Composite pattern is also useful in combination with other patterns that need to manage data structures. More typically, the Composite pattern will use the Flyweight pattern, rather than the other way around.
The division of intrinsic versus extrinsic state in the Flyweight pattern has a parallel with the virtual Proxy pattern. The original intent of the Flyweight pattern was that the extrinsic state would be computed from the intrinsic state. In our example of the Photo Group application, we did not so much compute the extrinsic state as use the name of the photo to fetch it from disk. The Proxy pattern has a similar feel to it in that, given a starting point, we can go to the disk and fetch the full item required. One difference is that the Proxy pattern deals with an object individually, whereas the Flyweight pattern from the start works with a dictionary of objects. These observations are summarized in .
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Structural Patterns: Adapter and Façade
The main pattern we will consider in this chapter is the Adapter pattern. It is a versatile pattern that joins together types that were not designed to work with each other. It is one of those patterns that comes in useful when dealing with legacy code—i.e., code that was written a while ago and to which one might not have access. There are different kinds of adapters, including class, object, two-way, and pluggable. We'll explore the differences here. The second pattern we will look at in this chapter—the Façade pattern—is a simple one that rounds out the structural group. The aim of this pattern is to provide a simplified interface to a set of complex systems.
The Adapter pattern enables a system to use classes whose interfaces don't quite match its requirements. It is especially useful for off-the-shelf code, for toolkits, and for libraries. Many examples of the Adapter pattern involve input/output because that is one domain that is constantly changing. For example, programs written in the 1980s will have very different user interfaces from those written in the 2000s. Being able to adapt those parts of the system to new hardware facilities would be much more cost effective than rewriting them.
Toolkits also need adapters. Although they are designed for reuse, not all applications will want to use the interfaces that toolkits provide; some might prefer to stick to a well-known, domain-specific interface. In such cases, the adapter can accept calls from the application and transform them into calls on toolkit methods.
Our illustration of the Adapter pattern is a very real one—it involves hardware instruction sets, not input/output. From 1996 to 2006, Apple Macintosh computers ran on the PowerPC processor. The operating system was Mac OS X. But in April 2006, Apple started releasing all new Apple computers—iMacs, Minis, and MacBooks—with Intel Core Duo processors. Mac OS X was rewritten to target the new processor, and users of the new computers mostly accessed existing Intel-based software via other operating systems, such as Linux and Windows. shows iMacs made in 1998 and 2006.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Adapter Pattern
The Adapter pattern enables a system to use classes whose interfaces don't quite match its requirements. It is especially useful for off-the-shelf code, for toolkits, and for libraries. Many examples of the Adapter pattern involve input/output because that is one domain that is constantly changing. For example, programs written in the 1980s will have very different user interfaces from those written in the 2000s. Being able to adapt those parts of the system to new hardware facilities would be much more cost effective than rewriting them.
Toolkits also need adapters. Although they are designed for reuse, not all applications will want to use the interfaces that toolkits provide; some might prefer to stick to a well-known, domain-specific interface. In such cases, the adapter can accept calls from the application and transform them into calls on toolkit methods.
Our illustration of the Adapter pattern is a very real one—it involves hardware instruction sets, not input/output. From 1996 to 2006, Apple Macintosh computers ran on the PowerPC processor. The operating system was Mac OS X. But in April 2006, Apple started releasing all new Apple computers—iMacs, Minis, and MacBooks—with Intel Core Duo processors. Mac OS X was rewritten to target the new processor, and users of the new computers mostly accessed existing Intel-based software via other operating systems, such as Linux and Windows. shows iMacs made in 1998 and 2006.
Figure : Adapter pattern illustration—migration of Mac OS X from a 1998 PowerPC-based iMac to a 2007 Intel-based iMac
Mac OS X was originally designed to take advantage of the AltiVec floating-point and integer SIMD instruction set that is part of the PowerPC processor. When the Intel processor replaced this processor, calls to AltiVec instructions from Mac OS X had to be retargeted to the Intel x86 SSE extensions, which provide similar functionality to AltiVec.
For something as important as an operating system, the code could be rewritten to replace the calls to AltiVec with calls to SSE. However, Apple recognized that application developers might not want to do this, or might not have access to the source of old AltiVec-based code, so they recommended the use of the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Façade Pattern
The role of the Façade pattern is to provide different high-level views of subsystems whose details are hidden from users. In general, the operations that might be desirable from a user's perspective could be made up of different selections of parts of the subsystems.
Simple interfaces to complex subsystems abound in real life. They can be created to make frequent use of a system faster, or to differentiate between novices and power users. A good illustration is Amazon.com's 1-Click® system (), which simplifies the process of ordering items for well-known customers. Normally, after selecting an item to purchase, an Amazon customer has to enter delivery and bank account details before the order is accepted. If these details are stored and the customer verifies her identity in some way, 1-Click takes that customer straight to the checkout. The customer's stored bank account details and selected delivery address are used for the purchase, thus considerably speeding up (and simplifying) the ordering process. Thus, the 1-Click option forms a façade to the fuller system underneath.
Figure : Façade pattern illustration—1-Click®
Hiding detail is a key programming concept. What makes the Façade pattern different from, say, the Decorator or Adapter patterns is that the interface it builds up can be entirely new. It is not coupled to existing requirements, nor must it conform to existing interfaces. There can also be several façades built up around an existing set of subsystems. The term "subsystem" is used here deliberately; we are talking at a higher level than classes. See the UML diagram in ; it considers the subsystems to be grouped together, so they can interact in agreed ways to form the top-level operations.
Figure : Façade pattern UML diagram
The roles are:
Namespace 1
A library of subsystems
Subsystem
A class offering detailed operations
Façade
A class offering a few high-level operations as selections from the subsystems
Namespace 2
Where the client resides
Client
Calls the high-level operations in the Façade in Namespace 1
As shown in the UML diagram, the client's code does not make reference to the classes of the names of the subsystems; it only gets access to their operations via the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Pattern Comparison
The Adapter pattern has much in common with the patterns discussed in . The differences are in the intents of the patterns. A bridge, for example, separates an interface and its implementation so that they can vary independently, whereas an adapter changes the interface of an existing object. The Adapter pattern is more useful for one-off changes, whereas the Bridge pattern anticipates that change might happen continuously.
A decorator enhances an object without changing its interface and should be transparent to the application. An adapter is not transparent, as it is the named implementation of the interface the client sees. The Proxy pattern does not change any interfaces; it defines substitute objects for other objects.
From a certain point of view, the Façade pattern is also adapting requests: it transforms high-level requests into a sequence of lower-level requests. The Façade's intent is to hide complexity, and the Façade subsystems are not intended to be accessible by the client.
To complete the picture, we can classify the Adapter and Façade patterns according to the mechanisms shown in .
Table : Comparison of Adapter and Façade patterns
Mechanism
Adapter
Façade
Original
Adaptee
SubsystemA, B, and C
Interface
ITarget
Façade
New
Adapter
Operation1 and 2
Client
Aggregates ITarget
Accesses Façade
Client activates
New
New
Original changed by
No change
No change
New classes/subsystems
Adapter provides adaptations to their methods
Façade supplies high-level operations
Operation routed
From new to original
From new to original
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 5: Creational Patterns: Prototype, Factory Method, and Singleton
The creational patterns aim to separate a system from how its objects are created, composed, and represented. They increase the system's flexibility in terms of the what, who, how, and when of object creation. Creational patterns encapsulate the knowledge about which classes a system uses, but they hide the details of how the instances of these classes are created and put together. Programmers have come to realize that composing systems with inheritance makes those systems too rigid. The creational patterns are designed to break this close coupling. In this and the following chapter, we shall make further use of some C# features that help to abstract the instantiation process—generics and delegates (introduced in and , respectively) are two of these.
We'll start by looking at three small patterns that are helpful in combination with many others. The Prototype pattern ensures that when copies of complex objects are made, they are true copies. The Factory Method pattern is a means of creating objects without knowing the exact subclass being used. Finally, the Singleton pattern ensures that only one of a class can be built and that all users are directed to it.
The Prototype pattern creates new objects by cloning one of a few stored prototypes. The Prototype pattern has two advantages: it speeds up the instantiation of very large, dynamically loaded classes (when copying objects is faster), and it keeps a record of identifiable parts of a large data structure that can be copied without knowing the subclass from which they were created.
Let's again consider the Photo Group application discussed in , which held groups of photographs (see ). At some stage, we might like to archive one of the groups by copying it to another album. Then, later, we can bring it back again (perhaps if the original is deleted by mistake). In this case, the archive becomes a holder of prototypes that can be copied whenever required. We shall call the updated version of the application with this added functionality Photo Archive.
Objects are usually instantiated from classes that are part of the program. The Prototype pattern presents an alternative route by creating objects from existing prototypes. The UML for the Prototype pattern is given in .
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Prototype Pattern
The Prototype pattern creates new objects by cloning one of a few stored prototypes. The Prototype pattern has two advantages: it speeds up the instantiation of very large, dynamically loaded classes (when copying objects is faster), and it keeps a record of identifiable parts of a large data structure that can be copied without knowing the subclass from which they were created.
Let's again consider the Photo Group application discussed in , which held groups of photographs (see ). At some stage, we might like to archive one of the groups by copying it to another album. Then, later, we can bring it back again (perhaps if the original is deleted by mistake). In this case, the archive becomes a holder of prototypes that can be copied whenever required. We shall call the updated version of the application with this added functionality Photo Archive.
Objects are usually instantiated from classes that are part of the program. The Prototype pattern presents an alternative route by creating objects from existing prototypes. The UML for the Prototype pattern is given in .
Figure : Prototype pa