msdn design pattern.doc

14
Discover the Design Patterns You're Already Using in the .NET Framework Microsoft has placed increasing emphasis on design patterns. If you are unfamiliar with patterns, suddenly being inundated with new terms and foreign-looking UML diagrams can be overwhelming. This emphasis on patterns, however, doesn't represent a shift in methodology so much as a change in vocabulary. The Microsoft ® .NET Framework base class library (BCL) already makes extensive use of patterns, and you are probably familiar with the most common ones, even though you might not realize it yet. In this article, I'll cover a basic overview of several common design patterns and how they are used in the BCL and other areas of the .NET Framework. In doing so, you can discover some of the motivation for why the Framework is designed the way it is, as well as make the abstract concepts of the patterns themselves more intuitively understandable. Most of the patterns I'll be covering come from the canonical reference, Design Patterns by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, (Addison-Wesley, 1995). These authors are collectively known as the Gang of Four. The Gang of Four didn't invent these patterns, but they documented and formalized the good work others had been doing since the beginning of software development. If you are already familiar with some of these patterns, feel free to read about those you aren't familiar with, since each section is relatively independent. The section on ASP.NET-related patterns requires familiarity with the request pipeline, a basic overview of which is provided in that section. Within the .NET Framework, the use of some patterns is so prevalent that they have been built into programming languages themselves, instead of just represented by the class libraries. The first two patterns I will discuss, the Observer and the Iterator, are supported by various language features of both C# and Visual Basic ® .NET. As you will see, they are an integral part of many common programming tasks. Observer Pattern Good object-oriented design emphasizes both encapsulation and loose coupling. In other words, classes should keep internal details private and also minimize their strict dependencies. In most applications, classes don't work in isolation; they interact with many other classes. A common scenario of class interaction occurs when one class (the Observer) needs to be notified when something changes in another (the Subject). For

Upload: sundari-murugan

Post on 14-Feb-2015

14 views

Category:

Documents


0 download

DESCRIPTION

MSDN Design Pattern

TRANSCRIPT

Page 1: MSDN Design Pattern.doc

Discover the Design Patterns You're Already Using in the .NET Framework

Microsoft has placed increasing emphasis on design patterns. If you are unfamiliar with patterns, suddenly

being inundated with new terms and foreign-looking UML diagrams can be overwhelming. This emphasis

on patterns, however, doesn't represent a shift in methodology so much as a change in vocabulary. The

Microsoft® .NET Framework base class library (BCL) already makes extensive use of patterns, and you are

probably familiar with the most common ones, even though you might not realize it yet.

In this article, I'll cover a basic overview of several common design patterns and how they are used in

the BCL and other areas of the .NET Framework. In doing so, you can discover some of the motivation for

why the Framework is designed the way it is, as well as make the abstract concepts of the patterns

themselves more intuitively understandable.

Most of the patterns I'll be covering come from the canonical reference, Design Patterns by Erich

Gamma, Richard Helm, Ralph Johnson, and John Vlissides, (Addison-Wesley, 1995). These authors are

collectively known as the Gang of Four. The Gang of Four didn't invent these patterns, but they

documented and formalized the good work others had been doing since the beginning of software

development.

If you are already familiar with some of these patterns, feel free to read about those you aren't familiar

with, since each section is relatively independent. The section on ASP.NET-related patterns requires

familiarity with the request pipeline, a basic overview of which is provided in that section.

Within the .NET Framework, the use of some patterns is so prevalent that they have been built into

programming languages themselves, instead of just represented by the class libraries. The first two

patterns I will discuss, the Observer and the Iterator, are supported by various language features of both

C# and Visual Basic® .NET. As you will see, they are an integral part of many common programming tasks.

Observer Pattern

Good object-oriented design emphasizes both encapsulation and loose coupling. In other words, classes

should keep internal details private and also minimize their strict dependencies. In most applications,

classes don't work in isolation; they interact with many other classes. A common scenario of class

interaction occurs when one class (the Observer) needs to be notified when something changes in another

(the Subject). For example, several Windows® Forms controls might need to update their display after a

button is clicked. A simple solution would be to have the Subject call a specific method of the Observer

whenever a change in state occurs. This introduces a host of problems, however. Now, since the Subject

needs to know which method to call, it is tightly coupled to that specific Observer. Furthermore, if you

need to add more than one Observer, you have to continue to add code for each method call to the

Page 2: MSDN Design Pattern.doc

Subject. If the number of Observers changes dynamically, this gets even more complex. You'll quickly end

up with a brittle mess that's difficult to maintain.

Applying the Observer pattern helps to resolve this problem efficiently. You can decouple the Subject

from the Observers so that Observers of any variety can easily be added and removed, at both design time

and run time. The Subject maintains a list of interested Observers. Each time the Subject's state changes,

it calls the Notify method on each Observer. Figure   1 shows a sample implementation. All classes

designed to act as Observers implement the ICanonicalObserver interface, and all Subjects must derive

from CanonicalSubjectBase. If a new Observer wants to monitor a Subject, the Add method easily handles

this without having to change any code in the Subject class. Note also that each Subject only directly

depends on the ICanonicalObserver interface, not any specific Observer.

While the Gang of Four's Observer pattern solves some of these problems, there are still some

roadblocks, since subjects must inherit from a specific base class and Observers must implement a special

interface. Thinking back to the Windows Forms button example, a solution emerges. The .NET Framework

introduces delegates and events to solve these problems. If you have done any programming for ASP.NET

or Windows Forms, then you have probably worked with events and event handlers. Events act as the

Subject, while delegates act as Observers. Figure   2 shows an example of the Observer pattern, making

use of events.

The Windows Forms Button control exposes a Click event which gets raised whenever the button is

clicked. Any class designed to react to this event just needs to register a delegate with that event. The

Button class doesn't depend on any of the potential Observers, and each Observer only needs to know the

correct type of the delegate for the event (EventHandler, in this case). Since EventHandler is a delegate

type and not an interface, each Observer doesn't need to implement an extra interface. Assuming it

already contains a method with a compatible signature, it only needs to register that method with the

event of the Subject. Through delegates and events, the Observer pattern lets you decouple Subjects and

their Observers.

Back to Contents

Iterator Pattern

Many programming tasks involve manipulating collections of objects. Whether these collections are

simple lists or something more complex such as binary trees, you'll often need some way to get access to

each object in the collection. In fact, depending on the collection, you may want several ways to access

each object such as front to back, back to front, preorder or postorder. To keep the collection simple, the

traversal code itself is often in its own class.

One of the most basic ways to store a list of objects is in an array. Array types are built into both Visual

Basic .NET and C#. Both languages also have a loop structure that aids in iterating over arrays: foreach in

C# and For Each in Visual Basic .NET. Here's an easy example of iterating over arrays:

Page 3: MSDN Design Pattern.doc

int[] values = new int[] {1, 2, 3, 4, 5};

foreach(int i in values)

{

Console.Write(i.ToString() + " ");

}

These statements make use of the iterator for the array behind the scenes. All you need to know is that

you are guaranteed to have the loop run exactly once for each item in the array.

To make those statements work, the object referenced in the In expression must implement

IEnumerable. Any collection of objects that implements the IEnumerable interface can be traversed

(enumerated). This interface has a single method, GetEnumerator, that returns an object which

implements IEnumerator. The IEnumerator class contains the code necessary to iterate through the

collection. It has a property for the current object (Current) and methods to advance to the next object as

well as start over (MoveNext, and Reset). All of the collection classes in the System.Collections

namespace, as well as arrays, implement IEnumerable and can therefore be iterated over.

If you examine the Microsoft intermediate language (MSIL) generated by the C# compiler for any code

that uses foreach, you can see that in most cases it just uses the IEnumerator to do the iteration (certain

types, such as arrays and strings, are special cased by the compiler). Here you can see the IEnumerator

approach for iterating over the same array shown previously:

int[] values = new int[] {1, 2, 3, 4, 5};

IEnumerator e = ((IEnumerable)values).GetEnumerator();

while(e.MoveNext())

{

Console.Write(e.Current.ToString() + " ");

}

The .NET Framework uses the IEnumerable and IEnumerator interfaces to implement the Iterator

pattern. The Iterator pattern lets you easily traverse a collection without exposing the inner workings of

that collection. An Iterator class, an implementer of IEnumerator, is a separate class from the collection,

which implements IEnumerable. The Iterator class maintains the state of the traversal (including what the

current item is and whether or not there are more items to be traversed) outside of the collection itself.

The algorithm for the traversal is contained in the Iterator as well. This way you can simultaneously have

Page 4: MSDN Design Pattern.doc

multiple Iterators, each traversing the same collection in wildly different ways, without adding any

complexity to the collection class itself.

Back to Contents

Decorator Pattern

Any useful executable program involves either reading input, writing output, or both. Regardless of the

source of the data being read or written, it can be treated abstractly as a sequence of bytes. .NET uses the

System.IO.Stream class to represent this abstraction. Whether the data involves characters in a text file,

TCP/IP network traffic, or something else entirely, chances are you will have access to it via a Stream.

Since the class for working with file data (FileStream) and the class for working with network traffic

(NetworkStream) both inherit from Stream, you can easily write code that processes the data independent

of its origins. Here's a method for printing out some bytes from a Stream to the console:

public static void PrintBytes(Stream s)

{

int b;

while((b = fs.ReadByte()) >= 0)

{

Console.Write(b + " ");

}

}

Reading a single byte at a time is typically not the most efficient way to access a stream. For example,

hard drives are capable of (and optimized for) reading continuous blocks of data from the disk in a big

chunk. If you know you are going to be reading several characters, it is better to read a chunk from the

disk all at once and then consume the chunk from memory byte by byte. The Framework includes the

BufferedStream class for doing just that. The constructor for BufferedStream takes as the parameter

whatever stream you would like buffered access to. BufferedStream overrides the main methods of

Stream, such as Read and Write, to provide more functionality. Since it is still a child class of Stream, you

can use it the same as any other Stream (note that FileStream includes its own buffering capabilities).

Similarly, you can use System.Security.Cryptography.CryptoStream to encrypt and decrypt Streams on the

fly, without the rest of the application needing to know anything more than the fact that it is a Stream.

Figure   3 shows several calls to my printing method using different Streams.

Page 5: MSDN Design Pattern.doc

Figure 4 Using the Decorator Pattern

This ability to dynamically attach new functionality to objects transparently using composition is an

example of the Decorator pattern, as shown in Figure 4. Given any instance of Stream, you can add the

capability for buffered access by wrapping it in a BufferedStream, without changing the interface to the

data. Since you are just composing objects, this can be done at run time, rather than using a technique

like inheritance, which is a compile-time decision. The core functionality is defined either by an interface or

by an abstract class (like Stream) from which all the Decorators derive. The Decorators themselves

implement (or override) the methods in the interface (or base class) to provide the extra functionality.

BufferedStream, for example, overrides Read to read from a buffer fed by the wrapped Stream, instead of

reading from that Stream directly. As Figure   3 illustrates, any composition of Decorators, no matter how

complex, can still be used as if it were only the base class.

Back to Contents

Adapter Pattern

One of the strengths of the .NET Framework is backward compatibility. From .NET-based code you can

easily call legacy COM objects and vice versa. In order to use a COM component in your project, all you

have to do is add a reference to it via the Add Reference dialog in Visual Studio .NET. Behind the scenes,

Visual Studio® .NET invokes the tlbimp.exe tool to create a Runtime Callable Wrapper (RCW) class,

contained in an interop assembly. Once the reference has been added (and the interop assembly has been

generated for you), the COM component can be used like any other class in managed code. If you were

looking at code someone else had written without seeing the list of references (and without examining

metadata associated with the classes or their implementation), you would be unable to tell which classes

were written in a .NET-targeted language and which were COM components.

The magic that makes this happen is contained in the RCW. COM components have different error

handling mechanisms and also make use of different data types. For example, strings in the .NET

Framework use the System.String class while COM might use a BSTR. When calling a COM component with

a string parameter from .NET-based code, though, you can pass in a System.String just like you would to

any other similar managed code method. Inside the RCW, this System.String is converted into a format

Page 6: MSDN Design Pattern.doc

that the COM component expects, like a BSTR, before the COM call is made. Similarly, a method call on a

COM component typically returns an HRESULT to indicate success or failure. When a COM method call

returns an HRESULT that indicates that the call failed, the RCW turns this into an exception (by default), so

it can be handled like all other managed code errors.

By allowing managed classes and COM components to interact despite their interface differences, RCWs

are an example of the Adapter pattern. The Adapter pattern lets you adapt one interface to another. COM

doesn't understand the System.String class, so the RCW adapts it to something that it can understand.

Even though you can't change how a legacy component works, you can still interact with it. Adapters are

frequently used like this.

The Adapter class itself wraps an Adaptee, translating all calls from the client into the appropriate

format and sequence of calls. Though this sounds similar to the Decorator, there are several key

differences. With a Decorator, the interfaces of the objects you're composing are the same, while the

entire point of an Adapter is to allow you to change interfaces.Adapters also have a definite sequence to

them; the Adaptee must be contained by the Adapter. A Decorator class doesn't need to know whether it is

wrapped by 1 or 500 other classes, since the interfaces are all the same. As a result, the use of Decorators

can be transparent to the application, while the use of Adapter cannot.

Back to Contents

Factory Pattern

There are many cases in the Framework where you can obtain a new instance of a struct or class

without calling its constructor yourself. The System.Convert class contains a host of static methods that

work like this. To convert an integer to a Boolean, for example, you can call Convert.ToBoolean and pass in

the integer. The return value of this method call is a new Boolean set to "true" if the integer was non-zero

and "false" otherwise. The Convert class creates the Boolean for you with the correct value. Other type

conversion methods work similarly. The Parse methods on Int32 and Double return new instances of those

objects set to the appropriate value given only a string.

This strategy for creating new object instances is known as a Factory pattern. Rather than invoking the

object's constructor, you can ask the object factory to create the instance for you. That way, the factory

class can hide the complexity of object creation (like how to parse a Double out of a string). If you wanted

to change the details of creating the object, you'd only have to change the factory itself; you would not

have to change every single place in the code where the constructor is called.

These type conversion methods are one variant of this pattern, since you're not required to use the

factory to create the objects in question. A more pure example of this pattern is the

System.Net.WebRequest class, used to make a request and receive a response from a resource on the

Internet. FTP, HTTP, and file system requests are supported by default. To create a request, call the Create

method and pass in a URI. The Create method itself determines the appropriate protocol for the request

Page 7: MSDN Design Pattern.doc

and returns the appropriate subclass of WebRequest: HttpWebRequest, FtpWebRequest (new to the .NET

Framework 2.0), or FileWebRequest. The caller doesn't need to know the specifics of each protocol, only

how to invoke the factory and work with the WebRequest that gets returned. If the URI changes from an

HTTP address to an FTP address, the code won't have to change at all. This is another common use of the

Factory pattern. The parent class acts as a factory and returns a specific derived class based on

parameters the client passes in. As in the WebRequest example, this hides the complexity of selecting an

appropriate derived class from the caller.

Back to Contents

Strategy Pattern

Both Array and ArrayList provide the capability to sort the objects contained in the collection via the

Sort method. In fact, ArrayList.Sort just calls Sort on the underlying array. These methods use the

QuickSort algorithm. By default, the Sort method will use the IComparable implementation for each

element to handle the comparisons necessary for sorting. Sometimes, though, it is useful to sort the same

list in different ways. For example, arrays of strings might be sorted with or without case sensitivity. To

accomplish this, an overload of Sort exists that takes an IComparer as a parameter; IComparer.Compare is

then used for the comparisons. This overload allows users of the class to use any of the built-in IComparers

or any of their own making, without having to change or even know the implementation details of Array,

ArrayList, or the QuickSort algorithm.

Leaving the choice of comparison algorithm up to the user of the class like this is an example of the

Strategy pattern. The use of Strategy lets a variety of different algorithms be used interchangeably.

QuickSort itself only requires a way to compare objects to each other. By calling Compare through a

provided interface, the caller is free to substitute whatever particular comparison algorithm fits its specific

needs. The code for the QuickSort can remain unchanged.

Figure 5 Strategy in Action

One of the new generic collections in version 2.0 of the .NET Framework, List<T>, also makes heavy

use of the Strategy pattern, shown in Figure 5. In addition to the updated Sort method, the find-related

methods, BinarySearch, and others all take parameters that allow parts of the respective algorithms to

vary based on the needs of the caller. The use of a Predicate<T> delegate in the FindAll<T> method lets

the caller use any method as a filter for the List<T> so long as it takes the appropriate object type and

returns a Boolean. Combined with anonymous methods (another new C# language feature in version 2.0),

clients can easily filter lists based on properties and methods of the objects in the list, without introducing

Page 8: MSDN Design Pattern.doc

dependencies into the List<T> class itself. Using the Strategy pattern lets complicated processes like

sorting be easily modified to fit a caller's specific purpose, meaning you'll be able to write and maintain

less code.

Back to Contents

Composite Pattern in ASP.NET

The ASP.NET request/response pipeline is a complex system. Patterns are used in the design of the

pipeline itself and in the control architecture to effectively balance its performance with extensibility and

ease of programming. Before delving into the pipeline, however, I'll examine the patterns used in the

programming model itself.

When dealing with collections of objects, there are often operations that are appropriate for both a

single object and the entire collection. Think about an ASP.NET control. A control may be a simple single

item like a Literal, or it could be composed of a complex collection of child controls, like a DataGrid is.

Regardless, calling the Render method on either of these controls should still perform the same intuitive

function.

When each item in the collection might itself contain collections of other objects, the use of the

Composite pattern is appropriate. Composite is an easy way to represent tree-like collections without

having to treat parent and leaf nodes differently.

The canonical example of Composite relies on an abstract base class, Component, that contains both

methods for adding and removing children, and the operations common among parents and children.

ASP.NET uses this formulation exactly with System.Web.UI.Control. Control represents the Component

base class. It has operations for dealing with children (such as the child Controls property) as well as

standard operations and properties like Render and Visible. Each object, whether a primitive object (like

Literal) or a composite of several objects (like DataGrid), inherits from this base class.

Because the domain of controls is so diverse, there are several intermediate derived classes like

WebControl and BaseDataList that serve as base classes for other controls. Though these classes expose

additional properties and methods, they still retain the child management functions and core operations

inherited from Control. In fact, the use of the Composite pattern helps to hide their complexity, if desired.

Regardless of whether a control is a Literal or a DataGrid, the use of Composite means you can just call

Render and everything will sort itself out.

Back to Contents

Template Method Pattern

When the standard library of ASP.NET controls doesn't meet your needs, you have several options on

how to create your own. For simple controls that only need to be used in a single project, a user control is

Page 9: MSDN Design Pattern.doc

often the best choice. When the control is to be used in several Web applications or requires more

functionality, a custom server control may be a better fit.

When dealing with custom controls, there are two general types: controls that combine the functionality

of several existing controls (called composite controls), and controls with a unique visual representation.

The process for creating both of these types is very similar. For composite controls, you create a new class

that inherits from one of the control base classes (like Control or WebControl) and then override the

CreateChildControls method. In this method you add the controls whose functionality you are combining to

the collection of child controls, called Controls. For other custom controls, you override Render instead and

use the HtmlTextWriter parameter to output the HTML for your control directly.

Regardless of which style of custom control you choose, you don't have to write any code to handle the

functionality that's common to all controls, like loading and saving ViewState at the right time, allowing

PostBack events to be handled, and making sure the control lifecycle events are raised in the correct

order. The main algorithm for how a control should be loaded, rendered, and unloaded is contained in the

control base class.

The specifics of your particular control are handled at well-defined places in the control algorithm (the

CreateChildControls or Render methods). This is an example of the Template Method pattern. The main

algorithm skeleton is defined in a base class and subclasses can then plug in their own details without

affecting the algorithm itself, as shown in Figure 6. A composite control and a custom control both share

the same general lifecycle, but they can end up with drastically different visual representations.

Figure 6 Template Method Pattern

This pattern is similar to the Strategy pattern. These two patterns differ in scope and in methodology.

Strategy is used to allow callers to vary an entire algorithm, like how to compare two objects, while

Template Method is used to vary steps in an algorithm. Because of this, Strategy is more coarsely grained.

There can be vast differences between different client implementations, while with Template Method the

algorithm remains fundamentally the same. The other main difference is that Strategy uses delegation

while Template Method uses inheritance. In the sorting example of Strategy, the comparison algorithm is

delegated to the IComparer parameter, but with custom controls you subclass the base and override

methods to make changes. Both, however, let you easily alter processes to fit your specific needs.

Back to Contents

Patterns in the ASP.NET Pipeline

When a client requests an ASPX page from the Web server, the request travels through many steps

before ultimately ending up as HTML displayed by the client's browser. First, the request is processed by

Page 10: MSDN Design Pattern.doc

IIS and routed to the appropriate ISAPI extension. The ISAPI extension for ASP.NET (aspnet_isapi.dll) routes

the request to the ASP.NET worker process.

Figure 7 ASP.NET Request Pipeline

At this point, the request begins to interact with classes that you are used to dealing with. The request

is passed to an HttpApplication. Usually this is the class created in the codebehind file for Global.asax. The

HttpApplication then passes the request through any number of HTTP Modules. These classes implement

the IHttpModule interface and have a chance to modify the request (or even halt the processing of it)

before it gets passed on to the next module. ASP.NET provides some standard modules which provide

functionality you're probably familiar with, including FormsAuthenticationModule,

PassportAuthenticationModule, WindowsAuthenticationModule, and SessionStateModule, all of which

provide exactly the functionality that their names imply.

Ultimately, the request ends up at an IHttpHandler, the most common of which is System.Web.UI.Page.

Inside the IHttpHandler.ProcessRequest method, Page raises appropriate events (like Init, Load, and

Render), handles ViewState, and provides the programming model for ASP.NET. Figure 7 shows an outline

of this process.

Several of the patterns employed by this process are more thoroughly documented in another standard

reference for patterns, Martin Fowler's Patterns of Enterprise Application Architecture (Addison-Wesley,

2002).

Back to Contents

Intercepting Filter Pattern

Once a request has made it into an HttpApplication, it passes through any number of IHttpModules.

Each module is independent and has only a limited amount of control over the order in which it is invoked.

The HttpApplication class exposes a sequence of events that get raised as the request makes its way

through processing. These events include BeginRequest, AuthenticateRequest, AuthorizeRequest, and

EndRequest. When the HttpApplication loads a module, it calls the Init method of the IHttpModule

interface, allowing the module to register for any of the events it cares about. As a given request is

Page 11: MSDN Design Pattern.doc

handled, the events are raised in the appropriate order and all registered modules get a chance to interact

with the request. The module can therefore control the stage at which it gets invoked, but not the exact

order within that stage.

These modules are an example of the Intercepting Filter pattern. This pattern represents a chain of

filters that are each in turn given a chance to modify a request (or message) that passes through them.

Figure 8 shows a simplified flow diagram of this process. The key ideas of this pattern are that the filters

are independent; filters can modify the request as it passes through.

Figure 8 Request Flow

There are several different implementation variations of the Intercepting Filter pattern, one of which is

the event-based model employed by ASP.NET. A simpler variant involves maintaining a list of filters and

iterating over it, invoking a method on each one in turn. This is how the Web Services Enhancements

(WSE) for ASP.NET Web services uses this pattern. Each filter either extends SoapInputFilter (for request

messages) or SoapOutputFilter (for responses), overriding the ProcessMessage method to perform the

work of the filter.

Another option is to implement Intercepting Filter via the Decorator pattern. Then each filter would

wrap its successor, performing preprocessing, invoking the successor, and then performing post-

processing. The chain would be built using recursive composition, from back to front. This pattern is used

to implement .NET Remoting channel sinks.

Regardless of implementation, the result is a dynamically configurable chain of independent filters.

Since they are independent, these filters can easily be reordered and reused in other applications.

Common tasks like authentication or logging can be encapsulated in a filter and used over and over. These

tasks can then be handled by the filter chain before a request even reaches the HttpHandler, keeping the

handler code cleaner.

Back to Contents

Page Controller Pattern

System.Web.UI.Page implements a core part of the programming model for ASP.NET. Whenever you

want to add a logical page to a Web application, you can create a Web Form (represented by an ASPX file

and its codebehind). You can then write code to handle the specific demands of the new page, whether

through handling page-level events, displaying a set of controls, or loading and manipulating data. Each

logical page in the application has a corresponding Web Form that controls its behavior and regulates its

presentation.

Page 12: MSDN Design Pattern.doc

The practice of having one controller for each logical page is an example of the Page Controller pattern.

This idea is fundamental to ASP.NET. When a logical page is requested via its URI, the ASP.NET runtime

resolves the address to the corresponding subclass of Page and uses that class to handle the request. All

the details about what a page looks like, what user input it can handle, and how it responds to that input

are contained in a single place. When a logical page in the application requires changes, no other pages

are affected. It's an abstraction that's so common we don't even think about it.

One of the drawbacks usually associated with the pure implementation of Page Controller is that

common code must be repeated for each page. ASP.NET avoids this drawback by including other patterns

in the pipeline implementation, as well as providing System.Web.UI.Page as a common base class for all

Page Controllers. Cross-cutting concerns like authentication and session state are handled by the

HttpModule Intercepting Filters and raising page lifecycle events, and other activities, are handled by the

base class.

Back to Contents

Other Web Presentation Patterns in ASP.NET

In addition to Intercepting Filter and Page Controller, ASP.NET makes use of variations of several other

Web presentation patterns. When ASP.NET determines which HttpHandler to pass a request to, it uses

something similar to a Front Controller. Front Controller is characterized by a single handler for all requests

(like System.Web.UI.Page). Once the request reaches the Page class, though, the Page Controller pattern

takes over.

Within the ASP.NET implementation of Page Controller, there are elements of the Model View Controller

pattern. Model View Controller separates the model (business objects, data, and processes) from the view

(the display of information). The controller responds to user input and updates the model and view.

Roughly speaking, an ASPX page represents the View while its codebehind file represents a hybrid Model-

Controller. If you were to pull all business and data-related code out of the codebehind and leave only

event handling code, this would turn the codebehind into a pure Controller while the new class containing

the business logic would be the Model.

Since these variations diverge from the canonical form of the patterns, they won't be covered here.

Martin Fowler's book and the Microsoft Patterns site are good sources for more discussion.

Back to Contents

Conclusion

Now that I have examined instances of common patterns in use throughout the .NET Framework and

BCL, it should be even easier to recognize those same patterns in code you work with every day. Hopefully

highlighting the design patterns underlying common classes and functionality has given you a better sense

of what those patterns are and the benefits they provide. Trying to imagine UI programming without the

Page 13: MSDN Design Pattern.doc

Observer pattern or collections without an Iterator shows how indispensable these frameworks really are.

Once you understand what each does, it becomes another valuable tool to add to your toolbox.