Extreme ASP.NET Makeover: Disentangling Our Tangled Web-Overview - Overview

Download

Right click “Save as…”

  • High Quality WMV (PC)
  • MP3 (Audio only)
  • MP4 (iPhone, Android)
  • Mid Quality WMV (Lo-band, Mobile)
  • WMV (WMV Video)

We’re now on the eighth installment of Extreme ASP.NET Makeover. In part 7, we examined the singleton pattern and refactored away the AuthChecker singleton. AuthorizationServices, which previously used the AuthChecker singleton, now depends on the IAuthorizationChecker interface and simply creates an instance of the AuthorizationChecker class in its constructor by calling the new operator. In this intallment, we will discuss better ways to manage dependencies in our applications using an inversion of control container. If you are not familiar with inversion of control containers or the related concepts of dependency inversion and dependency injection, take some time to read James’ article on those topics from the March 2008 issue of MSDN Magazine, “Loosen Up: Tame Your Software Dependencies for More Flexible Apps.”

The Tangled Web we Weave

Let’s take a closer look at AuthorizationServices. AuthorizationServices depends on IAuthorizationChecker, which is implemented by AuthorizationChecker. In turn, AuthorizationChecker depends on Settings to retrieve the configured ISettingsStorageProviderV30, which can be a SettingsStorageProvider, SqlSettingsStorageProvider, or other implementation. Now, SettingsStorageProvider depends on IHostV30 and its default implementation by Host. Host is a “God object” in ScrewTurn Wiki. According to Wikipedia, “a God object is an object that knows too much or  does too much.” A God object is a software anti-pattern.

Host is a coordinator of a wide variety of unrelated activities in ScrewTurn. It handles everything from retrieving settings values to sending emails to handling backups to finding users, Wiki pages, namespaces, categories, and more.

Many of these responsibilities are delegated to sub-objects, and Host only acts as the coordinator of these services. This is better than having all that functionality actually coded in Host itself. Host does still present a problem in that its delicate coordination of all the other objects in the system, especially during application startup, means that it is difficult to refactor the overall system. Moving startup code from one class to another causes breakages as the ordering of construction is slightly changed and previously working code throws NullReferenceExceptions as dependencies are unexpectedly null due to construction orderings.

Let’s take a look at Host’s constructor:

public Host() {
    customSpecialTags = new Dictionary<string, CustomToolbarItem>(5);
}

In its constructor, Host creates a single Dictionary. There is no indication that Host has a dependency problem. Host’s tangled web of dependencies is hidden due to its overuse of Singletons and static helper classes. For example, let’s take a look at a few Host methods:

public UserInfo[] GetUsers() { 
    return Users.Instance.GetUsers().ToArray();
}

public PageContent GetPageContent(PageInfo page) {
    return Content.GetPageContent(page, true);
}

public string Format(string raw) {
    return Formatter.Format(raw, FormattingContext.Unknown, null);
}

Note the use of the Users singleton and the static Content an Formatter classes in the above code. Without actually looking at Host’s implementation code, there is no way to tell that it depends on these other classes. To make matters even more complicated, Users (and other dependent classes) depend on Host! If constructors aren’t run in the correct order and before certain methods are called, NullReferenceExceptions will be your reward. All of the construction logic is carefully orchestrated by StartupTools, which is responsible for constructing and initializing all singletons within the application in the correct order.

Seeking Salvation

God objects lead us down a road unto madness. Host’s dependencies are so tangled and interdependent that the result is only somewhat better than a single, monolithic object. So how do we chart a course back to sanity?

Dependencies in software are not fundamentally the problem here. Without dependencies, we wouldn’t be able to write much more than “Hello, World!”-style applications. (Even a simple application like Hello, World!  requires dependencies on System.Console and System.String, as well as the CLR.) The problem is unchecked and unfettered dependencies between objects that do not need to know about each other. Before we can solve the problem of unnecessary dependencies, we need to make those dependencies obvious. How can we construct our classes/objects so that their dependencies are obvious? Make dependencies apparent by declaring them in the classes constructor. For example, let’s look at AuthorizationServices’ constructor:

public AuthorizationServices(IAuthorizationChecker authorizationChecker) {
    this.authorizationChecker = authorizationChecker;
}

Simply by looking at the constructor’s signature, we can tell that AuthorizationServices requires an IAuthorizationChecker to perform its work. (You probably already knew that because we implemented it in part 7.) The concrete implementation of IAuthorizationChecker is AuthorizationChecker:

public AuthorizationChecker() {
    settingsProvider = Settings.Instance.Provider;
}

Notice that you don’t know that AuthorizationChecker depends on the static Settings class. You have to read AuthorizationChecker’s constructor implementation to figure this out. While not hard, it is not immediately obvious that AuthorizationChecker depends on ISettingsStorageProviderV30, which is the type return by the Provider property. How can we make this more obvious? By declaring our intent in the constructor parameters, like so:

public AuthorizationChecker() : this(Settings.Instance.Provider) {
}

public AuthorizationChecker(ISettingsStorageProviderV30 settingsProvider) {
    if(settingsProvider == null)
        throw new ArgumentNullException("settingsProvider");

    this.settingsProvider = settingsProvider;
}

Our other code depends on the parameterless constructor. We will leave it in place for now and chain to an overloaded constructor that takes a ISettingsStorageProviderV30. Our dependency on ISettingsStorageProviderV30 has now been made obvious.

We are not out of the proverbial woods yet. Other dependencies are lurking within AuthorizationChecker, namely the static classes AuthTools and AclEvaluator. We can make them obvious dependencies by changing them from static classes to instances and moving their initialization to the constructor.

Other videos from this article

Read the full article at http://msdn.microsoft.com/magazine/ee424155.aspx

Tags:

Follow the Discussion

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.