Design Principles and Patterns for Software Engineering with Microsoft .NET

  • 10/15/2008

From Principles to Patterns

It is guaranteed that by fulfilling all the OOD principles just discussed, you can craft a good design that matches requirements and is maintainable and extensible. A seasoned development team, though, will not be limited to applying effective design principles over and over again; members of the team, in fact, will certainly draw from the well of their experience any solutions for similar problems that worked in the past.

Such building blocks are nothing more than hints and the skeleton of a solution. However, these very same building blocks can become more refined day after day and are generalized after each usage to become applicable to a wider range of problems and scenarios. Such building blocks might not provide a direct solution, but they usually help you to find your (right) way. And using them is usually more effective and faster than starting from scratch.

By the way, these building blocks are known as patterns.

What’s a Pattern, Anyway?

The word pattern is one of those overloaded terms that morphed from its common usage to assume a very specific meaning in computer science. According to the dictionary, a pattern is a template or model that can be used to generate things—any things. In computer science, we use patterns in design solutions at two levels: implementation and architecture.

At the highest level, two main families of software patterns are recognized: design patterns and architectural patterns. You look at design patterns when you dive into the implementation and design of the code. You look at architectural patterns when you fly high looking for the overall design of the system.

Let’s start with design patterns.

Design Patterns

We software professionals owe design patterns to an architect—a real architect, not a software architect. In the late 1970s, Christopher Alexander developed a pattern language with the purpose of letting individuals express their innate sense of design through a sort of informal grammar. From his work, here’s the definition of a pattern:

  • Each pattern describes a problem which occurs over and over again in our environment, and then describes the core solution to that problem, in such a way that you can use the solution a million times over, without ever doing it the same way twice.

Nicely enough, although the definition was not written with software development in mind, it applies perfectly to that. So what’s a design pattern?

A design pattern is a known and well-established core solution applicable to a family of concrete problems that might show up during implementation. A design pattern is a core solution and, as such, it might need adaptation to a specific context. This feature becomes a major strength when you consider that, in this way, the same pattern can be applied many times in many slightly different scenarios.

Design patterns are not created in a lab; quite the reverse. They originate from the real world and from the direct experience of developers and architects. You can think of a design pattern as a package that includes the description of a problem, a list of actors participating in the problem, and a practical solution.

The primary reference for design patterns is GoF. Another excellent reference we want to recommend is Pattern-Oriented Software Architecture by Frank Buschmann, et al. (Wiley, 1996).

How to Work with Design Patterns

Here is a list of what design patterns are not:

  • Design patterns are not the verb and should never be interpreted dogmatically.

  • Design patterns are not Superman and will never magically pop up to save a project in trouble.

  • Design patterns are neither the dark nor the light side of the Force. They might be with you, but they won’t provide you with any special extra power.

Design patterns are just helpful, and that should be enough.

You don’t choose a design pattern; the most appropriate design pattern normally emerges out of your refactoring steps. We could say that the pattern is buried under your classes, but digging it out is entirely up to you.

The wrong way to deal with design patterns is by going through a list of patterns and matching them to the problem. Instead, it works the other way around. You have a problem and you have to match the problem to the pattern. How can you do that? It’s quite simple to explain, but it’s not so easy to apply.

You have to understand the problem and generalize it.

If you can take the problem back to its roots, and get the gist of it, you’ll probably find a tailor-made pattern just waiting for you. Why is this so? Well, if you really reached the root of the problem, chances are that someone else did the same in the past 15 years (the period during which design patterns became more widely used). So the solution is probably just there for you to read and apply.

This observation prompts us to mention the way in which all members of our teams use books on design patterns. (By the way, there are always plenty of such books scattered throughout the office.) Design patterns books are an essential tool. But we never read such books. We use them, instead, like cookbooks.

What we normally do is stop reading after the first few pages precisely where most books list the patterns they cover in detail inside. Next, we put the book aside and possibly within reach. Whenever we encounter a problem, we try to generalize it, and then we flip through the pages of the book to find a pattern that possibly matches it. We find one much more often than not. And if we don’t, we repeat the process in an attempt to come to a better generalization of the problem.

When we’ve found the pattern, we start working on its adaptation to our context. This often requires refactoring of the code which, in turn, might lead to a more appropriate pattern. And the loop goes on.

Where’s the Value in Patterns, Exactly?

Many people would agree in principle that there’s plenty of value in design patterns. Fewer people, though, would be able to indicate what the value is and where it can be found.

Using design patterns, per se, doesn’t make your solution more valuable. What really matters, at the end of the day, is whether or not your solution works and meets requirements.

Armed with requirements and design principles, you are up to the task of solving a problem. On your way to the solution, though, a systematic application of design principles to the problem sooner or later takes you into the immediate neighborhood of a known design pattern. That’s a certainty because, ultimately, patterns are solutions that others have already found and catalogued.

At that point, you have a solution with some structural likeness to a known design pattern. It is up to you, then, to determine whether an explicit refactoring to that pattern will bring some added value to the solution. Basically, you have to decide whether or not the known pattern you’ve found represents a further, and desirable, refinement of your current solution. Don’t worry if your solution doesn’t match a pattern. It means that you have a solution that works and you’re happy with that. You’re just fine. You never want to change a winning solution!

In summary, patterns might be an end when you refactor according to them, and they might be a means when you face a problem that is clearly resolved by a particular pattern. Patterns are not an added value for your solution, but they are valuable for you as an architect or a developer looking for a solution.

Applied Design Patterns

We said a lot about design patterns, but we haven’t shown a single line of code or a concrete example. Patterns are everywhere, even if you don’t realize it. As we’ll see in a moment, sometimes patterns are buried in the language syntax—in which case, we’ll call them idioms.

Have you ever needed to use a global object (or a few global objects) to serve all requests to a given class? If you have, you used the Singleton pattern. The Singleton pattern is described as a way to ensure that a class has only one instance for which a global point of access is required. Here’s an example:

public class Helpers
{
  public static Helpers DefaultInstance = new Helpers();

  protected Helpers() {}

  public void DoWork()
  {
    .
    .
    .
  }

  public void DoMoreWork()
  {
    .
    .
    .
  }
}

In a consumer class, you take advantage of Helpers through the following syntax:

Helpers.DefaultInstance.DoWork();

Swarms of Visual Basic 6 developers have used the Singleton pattern for years probably without ever realizing it. The Singleton pattern is behind the default instance of Visual Basic 6 forms, as shown here:

Form1.Show()

The preceding code in Visual Basic 6 invokes the Show method on the default instance of the type Form1. In the source, there’s no explicit mention of the default instance only because of the tricks played by the Visual Basic runtime.

Another interesting pattern to briefly mention is the Strategy pattern. The pattern identifies a particular functionality that a class needs and can be hot-plugged into the class. The functionality is abstracted to an interface or a base class, and the Strategy-enabled class uses it through the abstraction, as shown here:

public class MyService
{
  // This is the replaceable strategy
  ILogger _logger;

  public MyService(ILogger logger)
  {
    this._logger = logger;
  }

  public void DoWork()
  {
    this._logger.Log("Begin method ...");
    .
    .
    .
    this._logger.Log("End method ...");
  }
}

The Strategy pattern is the canonical example used to illustrate the power of composition. The class MyService in the example benefits from the services of a logger component, but it depends only on an abstraction of it. The external logger component can be changed with ease and without risking breaking changes. Moreover, you can even change the component (for example, the strategy) on the fly. Try getting the same flexibility in a scenario where the implementation of the strategy object is hard-coded in the MyService class and you have to inherit a new class to change strategy. It’s just impossible to change strategy in that case without recompilation and redeployment.

Architectural Patterns

Architectural patterns capture key elements of software architecture and offer support for making hard-to-change decisions about the structure of the system. As we saw in Chapter 1. software architecture is mostly about decisions regarding design points that, unlike code design, are not subject to refactoring.

Architectural patterns are selected and applied very early in the course of design, and they influence various quality characteristics of the system, such as performance, security, maintenance, and extensibility.

Examples of architectural patterns are Layers and SOA for modeling the application structure, Model-View-Controller for the presentation, Domain Model and Service Layer for the business logic, and Peer-to-Peer for the network topology.

Patterns vs. Idioms

Software patterns indicate well-established solutions to recurring design problems. This means that developers end up coding their way to a given solution over and over again. And they might be repeatedly writing the same boilerplate code in a given programming language.

Sometimes specific features of a given programming language can help significantly in quickly and elegantly solving a recurring problem. That specific set of features is referred to as an idiom.

What’s an Idiom, Anyway?

An idiom is a pattern hard-coded in a programming language or implemented out of the box in a framework or technology.

Like a design pattern, an idiom represents a solution to a recurring problem. However, in the case of idioms, the solution to the problem doesn’t come through design techniques but merely by using the features of the programming language. Whereas a design pattern focuses on the object-oriented paradigm, an idiom focuses on the technology of the programming language.

An idiom is a way to take advantage of the language capabilities and obtain a desired behavior from the code. In general, an idiom refers to a very specific, common, and eye-catching piece of code that accomplishes a given operation—as simple as adding to a counter or as complex as the implementation of a design pattern.

In C#, for example, the ++ operator can be considered a programming idiom for the recurring task of adding to a counter variable. The same can be said for the as keyword when it comes to casting to a type and defaulting to null in case of failure.

Let’s see some more examples of programming idioms in C#.

Sample Idioms

Events are the canonical example of a programming idiom. Behind events, you find the Observer pattern. The pattern refers to a class that has the ability to notify registered observers of some internal states. Whenever a particular state is reached, the class loops through the list of registered observers and notifies each observer of the event. It does that using a contracted observer interface.

In languages such as C# or Visual Basic .NET that support event-driven programming, you find this pattern natively implemented and exposed through keywords. Consider the following code:

Button1.Click += new EventHandler(Button1_Click);

When it runs, a new “observer for the Click event” is added to the list maintained by object Button1. The observer in this case is a delegate—a special class wrapping a class method.

The interface through which observer and object communicate is the signature of the method wrapped by the delegate.

Similarly, the foreach keyword in C# (and For . . . Each in Visual Basic .NET) is a hard-coded version of the Iterator pattern. An iterator object accomplishes two main tasks: it retrieves a particular element within a collection and jumps to the next element. This is exactly what happens under the hood of the following code:

foreach(Customer customer in dataContext.Customers)
{
    // The variable customer references the current element in the collection.
    // Moving to the next element is implicit.
}

Finally, the most recent versions of C# and Visual Basic .NET—those shipping with the .NET Framework 3.5—also support a set of contextual keywords for Language Integrated Query (LINQ): from, select, in, orderby. When you apply the set of LINQ keywords to a database-oriented object model, you have LINQ-to-SQL. With LINQ-to-SQL, you ultimately use language keywords to query the content of a database. In other words, you programmatically define an object that represents a query and run it. This behavior is described by the Query Object pattern. And LINQ-to-SQL is a programming idiom for the pattern.

Idiomatic Design

We spent a lot of time pondering OOD principles and showing their benefits and applicability. We did it by reasoning in a general context and looking at the OO paradigm rather than by examining the concrete technology and platform. General principles are always valid and should always be given due consideration.

However, when you step inside the design, at some point you meet the technology. When this happens, you might need to review the way you apply principles in the context of the specific technology or platform you’re using. This is called idiomatic design.

As far as the .NET Framework is concerned, a set of idiomatic design rules exists under the name of Framework Design Guidelines. You can access them online from the following URL: http://msdn.microsoft.com/en-us/library/ms229042.aspx.

As an example, let’s go through a couple of these guidelines.

Idiomatic Design: Structures or Classes?

When defining a type in a C# .NET application, should you use struct or class? To start out, a struct is not inheritable. So if you need to derive new classes from the type, you must opt for a class rather than a structure. This said, a class is a reference type and is allocated on the heap. Memorywise, a reference type is managed by the garbage collector. Conversely, a struct is a value type; it is allocated on the stack and deallocated when it goes out of scope. Value types are generally less expensive than reference types to work with, but not when boxing is required. In the .NET Framework, boxing is the task of storing a value type in an object reference so that it can be used wherever an object is accepted. As an example, consider the ArrayList class. When you add, say, an Int32 (or a struct) to an ArrayList, the value is automatically boxed to an object. Done all the time, this extra work might change the balance between class and struct. Hence, the need of an official guideline on the theme shows up.

The guideline suggests that you always use a class unless the footprint of the type is below 16 bytes and the type is immutable. A type is immutable if the state of its instances never changes after they’ve been created. (The System.String type in the .NET Framework is immutable because a new string is created after each modification.) However, if the struct is going to be boxed frequently you might want to consider using a class anyway. (If you’re looking for the list of differences between structs and classes go here: http://msdn.microsoft.com/en-us/library/saxz13w4.aspx.)

Idiomatic Design: Do Not Use List<T> in Public Signatures

Another guideline we want to point out has to do with the List<T> type. Their use in the signature of public members is not recommended, as you can see in this blog post: http://blogs.gotdotnet.com/kcwalina/archive/2005/09/26/474010.aspx.

Why is this so?

One of the reasons behind the guideline is that List<T> is a rather bloated type with many members that are not relevant in many scenarios. This means that List<T> has low cohesion and to some extent violates the Single Responsibility Principle.

Another reason for not using List<T> in public signatures is that the class is unsealed, yes, but not specifically designed to be extended. This doesn’t mean, though, that the class is not LSP-safe. If you look at the source of the class, you can see that using List<T> is absolutely safe in any polymorphic context. The issue is that the class has no protected and virtual methods for inheritors to do something significant that alters the behavior of the class while preserving the core interface. The class is just not designed to be extended.

It is therefore recommended that you use IList<T>, or derived interfaces, in public signatures. Alternatively, use custom classes that directly implement IList<T>.

Dependency Injection

As a design principle, DIP states that higher level modules should depend on abstractions rather than on the concrete implementation of functionalities. Inversion of control (IoC) is an application of DIP that refers to situations where generic code controls the execution of more specific and external components.

In an IoC solution, you typically have a method whose code is filled with one or more stubs. The functionality of each stub is provided (statically or dynamically) by external components invoked through an abstract interface. Replacing any external components doesn’t affect the high-level method, as long as LSP and OCP are fulfilled. External components and the high-level method can be developed independently.

A real-world example of IoC is Windows shell extensions. Whenever the user right-clicks and selects Properties, Windows Explorer prepares a standard dialog box and then does a bit of IoC. It looks up the registry and finds out whether custom property page extensions have been registered. If any are registered, it talks to these extensions through a contracted interface and adds pages to the user dialog box.

Another real-world example of IoC is event-driven programming as originally offered by Visual Basic and now supported by Windows Forms and Web Forms. By writing a Button1_Click method and attaching it to the Click event of, say, the Button1 control, you essentially instruct the (reusable and generic) code of the Button class to call back your Button1_Click method any time the user clicks.

What is dependency injection (DI), then?

From DIP to Inversion of Control

For the purpose of this discussion, IoC and DI are synonyms. They are not always considered synonyms in literature, as sometimes you find IoC to be the principle and DI the application of the principle—namely, the pattern. In reality, IoC is historically a pattern based on DIP. The term dependency injection was coined by Martin Fowler later, as a way to further specialize the concept of inversion of control.

IoC/DI remains essentially a pattern that works by letting you pass high-level method references to helper components. This injection can happen in three ways. One way is via the constructor of the class to which the method belongs. We did just this in the implementation of the FinanceInfoService class. Another way consists of defining a method or a setter property on the class to which the method belongs. Finally, the class can implement an interface whose methods offer concrete implementations of the helper components to use.

Today, IoC/DI is often associated with special frameworks that offer a number of rather advanced features.

IoC Frameworks

Table 3-1 lists some of the most popular IoC frameworks available.

Table 3-1. Main IoC Frameworks

Framework

More Information

Castle Windsor

http://www.castleproject.org/container/index.html

Ninject

http://www.ninject.org

Spring.NET

http://www.springframework.net

StructureMap

http://structuremap.sourceforge.net/Default.htm

Unity

http://codeplex.com/unity

Note that Ninject is also available for Silverlight and the Compact Framework. In particular, Microsoft’s Unity Application Block (Unity for short) is a lightweight IoC container with support for constructor, property, and method call injection. It comes as part of the Enterprise Library 4.0. Let’s use that for our demos.

All IoC frameworks are built around a container object that, bound to some configuration information, resolves dependencies. The caller code instantiates the container and passes the desired interface as an argument. In response, the IoC/DI framework returns a concrete object that implements that interface.

IoC Containers in Action

Suppose you have a class that depends on a logger service, such as the class shown here:

public class Task
{
  ILogger _logger;
  public Task(ILogger logger)
  {
    this._logger = logger;
  }
  public void Execute()
  {
    this._logger.Log("Begin method ...");
    .
    .
    .
    this._logger.Log("End method ...");
  }
}

The Task class receives the logger component via the constructor, but how does it locate and instantiate the logger service? A simple and static new statement certainly works, and so does a factory. An IoC container is a much richer framework that supports a configuration section:

<configuration>
  <configSections>
    <section name="unity"
             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
                   Microsoft.Practices.Unity.Configuration" />
  </configSections>
  .
  .
  .
  <unity>
    <containers>
       <container>
          <types>
             <type type="ILogger, mdUtils"
                   mapTo="ManagedDesign.Tools.DbLogger, mdTools" />
           </types>
       </container>
    </containers>
  </unity>
</configuration>

The configuration file (app.config or web.config) contains mapping between interfaces and concrete types to be injected. Whenever the container gets a call for ILogger, it’ll return an instance of DbLogger:

IUnityContainer container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection)
                                    ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
ILogger logger = container.Resolve<ILogger>();
Task t = new Task(logger);
.
.
.

IoC/DI is extremely useful for testing purposes and for switching between implementations of internal components. Frameworks just make it simple and terrific. In Chapter 6. we’ll return to IoC/DI to show how to inject a data access layer (DAL) in the middle tier of a layered system.

To finish, here are a couple of brief remarks about IoC/DI containers. Through the configuration script, you can instruct the container to treat injected objects as singletons. This means, for example, that the container won’t create a new instance of DbLogger every time, but will reuse the same one. If the DbLogger class is thread safe, this is really a performance boost.

In addition, imagine that the constructor of DbLogger needs a reference to another type registered with the IoC/DI framework. The container will be able to resolve that dependency, too.