howarddierking

Extreme ASP.NET Makeover: Mr. Escher, Your Software is Ready - Refactoring Notification

Download this episode

Download Video

Description

Extreme ASP.NET Makeover: Mr. Escher, Your Software is Ready –Refactoring Notification

The circular dependency problem between Pages and Users is due to each class implementing part of a larger notification feature. Once again this was not obvious when the two classes were coupled together via Singleton instances. Although the Single Responsibility Principle (SRP) would encourage us to have a separate Notification class, our primary job right now is to disentangle Pages and Users. Once the responsibility for notifications has been moved to one class or the other, it can be separated more easily into a separate Notification class. Let’s work on moving it to Pages.

We’ll start by adding notification-related methods on IUsers to IPages (we won’t remove the methods from IUsers or Users yet):

public interface IPages {
    ...
    bool SetEmailNotification(UserInfo user, PageInfo page,
      bool pageChanges, bool discussionMessages);
    bool SetEmailNotification(UserInfo user, NamespaceInfo nspace, 
      bool pageChanges, bool discussionMessages);
    void GetEmailNotification(UserInfo user, PageInfo page, 
      out bool pageChanges, out bool discussionMessages);
    void GetEmailNotification(UserInfo user, NamespaceInfo nspace, 
      out bool pageChanges, out bool discussionMessages);
    UserInfo[] GetUsersToNotifyForPageChange(PageInfo page);
    UserInfo[] GetUsersToNotifyForDiscussionMessages(PageInfo page);
}

We will implement these methods on Pages by temporarily delegating to Users:

public bool SetEmailNotification(UserInfo user, PageInfo page,
                                 bool pageChanges, bool discussionMessages) {
    return users.SetEmailNotification(user, page, 
                                      pageChanges, discussionMessages);
}

The other methods are implemented similarly. We can now look for usages of those methods on IUsers and change them to reference the same methods on IPages instead. The only usages for the IUsers methods should now be by Pages and the IUsers methods can be removed. You’ll have to temporarily cast the IUsers instances in the delegating methods to Users as the methods no longer exist on the IUsers interface:

public bool SetEmailNotification(UserInfo user, PageInfo page,
                                 bool pageChanges, bool discussionMessages) {
    return ((Users)users).SetEmailNotification(user, page,
                                      pageChanges, discussionMessages);
}

Time to pack up the notification-related methods on Users and move them to their new home on Pages.

And That’s a Wrap

Simply by making the dependencies between classes explicit via dependency injection, we revealed a number of circular dependencies lurking in the ScrewTurn Wiki codebase. Circular dependencies between components effectively turn the individual components into one large super-structure, which is difficult to modify. Changes ripple through these super-structures and resulting in unexpected breakages after seemingly innocuous modifications.

Applying the same techniques to Host’s other dependencies, we remove Host’s use of singletons altogether. (For the curious reader, the code is available for download from MSDN Code Gallery for Extreme ASP.NET Makeover.) Host’s constructor signature now looks like this:

public Host(ISettings settings, IAuthWriter authWriter, IUsers users,
         IPages pages, ISnippets snippets, INavigationPaths navigationPaths)

Its dependencies are obvious in a way that they were not before. Host has a fair number of dependencies, but it is acting as a facade between ScrewTurn Wiki and the plug-ins. So it is not overly worrying.

We can start asking ourselves meta questions about our software:

  • Do these dependencies make sense?
  • Should some of these dependencies be combined/split?
  • How do the dependencies relate to one another and can those relationships be improved?

We can reason about the overall structure of our software and improve it because that structure is more apparent. In the end, our software becomes more flexible, more testable, and more resilient in the face of change.

Acknowledgements

I would like to thank Dario Solera, creator of ScrewTurn Wiki, for being brave and open in allowing me to use ScrewTurn Wiki as the brownfield application throughout this series. He has already taken the constructive criticism provided in earlier articles and used it to improve the latest builds of ScrewTurn Wiki. Kudos to Dario for permitting all of us to learn together out in the open.

Thank you to my two occasional co-authors, Kyle Baley and Donald Belcham, for their assistance on parts 6 and 7. Your insights were invaluable in pushing the series forward. Dario has agreed to allow ScrewTurn Wiki to be used as the sample brownfield application for Kyle and Donald’s upcoming book, Brownfield Application Development in .NET, published by Manning. If you want more information about improving brownfield applications, their book is an excellent resource.

A big thanks to Howard Dierking for giving me the latitude and encouragement to take the series where I thought would be most useful and practical. Thanks also to Gary Clarke and the rest of the MSDN Magazine staff for their helpful suggestions, patience, and quick turn-around while editing the series.

Finally, I would like to offer special thanks to my spouse, Risa Kawchuk, and my two sons, Daegan and Gareth, for their understanding in not having as much spouse/daddy time in the last few months as I’ve toiled away evenings and weekends on these articles and screencasts.

Other videos from this article

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

Embed

Format

Available formats for this video:

Actual format may change based on video formats available and browser capability.

    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.