Los Techies : Blogs about software and anything tech!

Separation of Concerns by example: Part 1


In the prelude to this series, I looked at a snippet of code that took the kitchen sink approach to concerns.  Here's what we started out with:

public class CustomerManager
{
    [DataObjectMethod(DataObjectMethodType.Select, false)]
    public static List<Customer> GetCustomers(int startRowIndex, int maximumRows)
    {
        List<Customer> customers = null;
        string key = "Customers_Customers_" + startRowIndex + "_" + maximumRows;

        if (HttpContext.Current.Cache[key] != null)
        {
            customers = (List<Customer>) HttpContext.Current.Cache[key];
        }
        else
        {
            customers =
                (
                    from
                        c in DataGateway.Context.Customers
                    orderby
                        c.CustomerID descending
                    select
                        c
                ).Skip(startRowIndex).Take(maximumRows).ToList();

            if ((customers != null) && (customers.Count > 0))
                HttpContext.Current.Cache.Insert(key, customers, null, DateTime.Now.AddDays(1), TimeSpan.Zero);
        }

        return customers;
    }
}

Before we can even think about separating the concerns out, we already have the design working against us.  I prefer a dependency inversion approach to separate concerns, as opposed to having the class or method instantiate them all at once.

When we have a static method, how can we give the dependencies to the class?  I can think of some clever ways, but clever is rarely simple.  I'd like to err on the side of simple.

Additionally, we don't want our domain services mixed in with presentation concerns, such as the DataObjectMethod attribute.  That is in place for the ObjectDataSource control, a presentation object.

So, first order of business, let's make this a plain old class, instead of a static class.

Refactoring away from static methods

Now, presentation-specific classes are just fine.  If our presentation layer needs static methods and some crazy attributes, good for them.

But I don't want those concerns bleeding down into my BLL domain layer.

To fix this, first we'll need to extract method and get all of the contents out of that method (Ctrl+Alt+M ftw):

public class CustomerManager
{
    [DataObjectMethod(DataObjectMethodType.Select, false)]
    public static List<Customer> GetCustomers(int startRowIndex, int maximumRows)
    {
        return FindAllCustomers(startRowIndex, maximumRows);
    }

    private static List<Customer> FindAllCustomers(int startRowIndex, int maximumRows)
    {
        List<Customer> customers = null;
        string key = "Customers_Customers_" + startRowIndex + "_" + maximumRows;

        if (HttpContext.Current.Cache[key] != null)
        {
            customers = (List<Customer>) HttpContext.Current.Cache[key];
        }
        else
        {
            customers =
                (
                    from
                        c in DataGateway.Context.Customers
                    orderby
                        c.CustomerID descending
                    select
                        c
                ).Skip(startRowIndex).Take(maximumRows).ToList();

            if ((customers != null) && (customers.Count > 0))
                HttpContext.Current.Cache.Insert(key, customers, null, DateTime.Now.AddDays(1), TimeSpan.Zero);
        }

        return customers;
    }
}

Now that we have a private method, we can use Move Method to move this method to a whole new class.  If this method used any other private (or public) methods, we'd have to deal with them one at a time.  For now, we'll have a new CustomerFinder application-level service class:

public class CustomerFinder
{
    public List<Customer> FindAllCustomers(int startRowIndex, int maximumRows)
    {
        List<Customer> customers = null;
        string key = "Customers_Customers_" + startRowIndex + "_" + maximumRows;

        if (HttpContext.Current.Cache[key] != null)
        {
            customers = (List<Customer>) HttpContext.Current.Cache[key];
        }
        else
        {
            customers =
                (
                    from
                        c in DataGateway.Context.Customers
                    orderby
                        c.CustomerID descending
                    select
                        c
                ).Skip(startRowIndex).Take(maximumRows).ToList();

            if ((customers != null) && (customers.Count > 0))
                HttpContext.Current.Cache.Insert(key, customers, null, DateTime.Now.AddDays(1), TimeSpan.Zero);
        }

        return customers;
    }
}

Now, we don't have any tests in place (this is a full-blown legacy app), so we're just focusing on dependency-breaking techniques.  These techniques preserve existing behavior, which is what we need when we don't have a safety net of unit tests in place.

The existing CustomerManager now needs to change, as it needs to use our new class.  No matter, we'll just instantiate a new CustomerFinder and use that:

public class CustomerManager
{
    [DataObjectMethod(DataObjectMethodType.Select, false)]
    public static List<Customer> GetCustomers(int startRowIndex, int maximumRows)
    {
        return new CustomerFinder().FindAllCustomers(startRowIndex, maximumRows);
    }
}

Now our presentation layer is just a very thin layer on top of our application and domain layer.  Which is good, as the presentation layer is the hardest layer to test (and the absolute slooooooowest).

Quick review

We want to change the CustomerManager, but it's a presentation layer class that we still need to service our user interface.  To get around this, we applied a series of refactorings to move the behavior to an application layer service, which we can now work against for future enhancements.  These refactorings included:

We did these in a way such the existing behavior of CustomerManager remained unchanged, but its responsibilities were moved to the CustomerFinder service.

Next time, we'll look at the CustomerFinder to see how we can remove some of the static dependencies (I think I saw a screencast somewhere on that one...)

Kick It on DotNetKicks.com
Posted Jun 19 2008, 10:12 PM by bogardj
Filed under:

Comments

Joshua Flanagan wrote re: Separation of Concerns by example: Part 1
on 06-19-2008 11:31 PM

This is a great idea for a series, and I have a suggestion that might make it a little more effective.

I assume the target audience is readers that are not yet used to this type of development, but are curious enough to learn. Chances are, they are not using ReSharper and references to things like Ctrl_Alt-M will be confusing. It may slow you down a bit, but I bet it would help the readers a lot if you suggest the default Visual Studio key mappings (Ctrl-R, Ctrl-M - I had to look it up) when available, or how to go about the refactoring manually if it is not available in VS.

Yes, everyone should be using a tool like ReSharper, but lets not make it a barrier to entry to understanding the practices.

Reflective Perspective - Chris Alcock » The Morning Brew #119 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #119
on 06-20-2008 1:52 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #119

Colin Jack wrote re: Separation of Concerns by example: Part 1
on 06-20-2008 2:41 AM

As Joshua said its a great idea for a series, looking forward to reading the rest.

Dew Droplet - June 20, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Droplet - June 20, 2008 | Alvin Ashcraft's Morning Dew
on 06-20-2008 10:59 AM

Pingback from  Dew Droplet - June 20, 2008 | Alvin Ashcraft's Morning Dew

Coding in an Igloo wrote Separation of Concerns
on 06-20-2008 12:02 PM

Separation of Concerns

Arjan`s World » LINKBLOG for June 20, 2008 wrote Arjan`s World &raquo; LINKBLOG for June 20, 2008
on 06-20-2008 2:20 PM

Pingback from  Arjan`s World    &raquo; LINKBLOG for June 20, 2008

Code Monkey Labs wrote Weekly Web Nuggets #17
on 06-20-2008 2:36 PM

General Separation of Concerns by Example, Part 1 : I find that examples are one of the best ways to learn a topic. As a follow up to an example of what not to do , Jimmy Bogard has written an excellent post about how separation of concerns should be

Duncan wrote re: Separation of Concerns by example: Part 1
on 06-22-2008 6:07 PM

Thanks, this looks like a great example, I am looking forward to the rest of the series, I like description of the refactoring to solve the static method problem.

DotNetKicks.com wrote Seperation of Concerns
on 06-22-2008 6:14 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

gOODiDEA.NET wrote Interesting Finds: 2008.06.23
on 06-22-2008 7:09 PM

.NET Dynamic Compilation How is my C# code converted into machine instructions Becoming a Jedi - Part

GrabBag wrote Separation of Concerns by example: Part 2
on 06-24-2008 8:17 AM

Separation of concerns is one of the fundamental tenets of good object-oriented design. Anyone can throw

GrabBag wrote Separation of Concerns by example: Part 3
on 06-26-2008 11:14 PM

We made quite a bit of progress separating out the concerns in Part 2, but there are still some issues

GrabBag wrote Separation of Concerns by example: Part 4
on 07-10-2008 7:57 AM

In the last part, we finally broke out the caching and data access concerns from our original class.

GrabBag wrote Separation of Concerns by example: Part 5
on 07-17-2008 8:23 AM

In our last example, disaster finally struck our quaint little application. A strange defect showed up

Tim Barcz wrote Irreducible Complexity and Evolutionary Design
on 09-02-2008 9:49 AM

In church last week we were talking about evolution when the term &quot;Irreducible Complexity&quot;

Community Blogs wrote Irreducible Complexity and Evolutionary Design
on 09-02-2008 10:03 AM

In church last week we were talking about evolution when the term &quot;Irreducible Complexity&quot;

eKnacks wrote re: Separation of Concerns by example: Part 1
on 12-09-2008 11:34 PM

You've been knacked. Keep up the good work.

Code Monkey Labs wrote Weekly Web Nuggets #17
on 02-22-2009 9:48 PM

General Separation of Concerns by Example, Part 1 : I find that examples are one of the best ways to learn a topic. As a follow up to an example of what not to do , Jimmy Bogard has written an excellent post about how separation of concerns should be

Flaker’s writing place » Separation of concerns (SOC) articles wrote Flaker&#8217;s writing place &raquo; Separation of concerns (SOC) articles
on 09-09-2009 10:14 PM

Pingback from  Flaker’s writing place » Separation of concerns (SOC) articles

pkehcjaeucf wrote re: Separation of Concerns by example: Part 1
on 10-28-2009 4:19 AM

redUer  <a href="vkenxctomraf.com/.../a>, [url=http://svqpgdhekjrd.com/]svqpgdhekjrd[/url], [link=http://cxygayhghuwm.com/]cxygayhghuwm[/link], http://enpqieutrjek.com/

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems