CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Raymond Lewallen

Framework Design, Agile Coach, President Oklahoma City Developers Group, Microsoft MVP C#, TDD, Continuous Integration, Patterns and Practices, Domain Driven Design, Speaker, VB.Net, C# and Sql Server

Asking for your ideas regarding refactoring a design smell

Logic can certainly destroy your code reuse, and produce quasi-design smells that you’d really like to do something about because it aggrevates you, but you can’t really change.  What if I told you that I had 9 different functions that determine if a date is one calendar year from today’s date?  You’d probably gasp, and wonder why in the heck I would need 9 different functions for that.  I’ll tell you.  In my business domain, 1 calendar year means the following:

Given today is 5/8/2006, 1 calendar year can mean, depending on the context:

5/1/2006 00:00 – 4/30/2007 23:59
5/1/2006 00:00 – 5/8/2007 23:59
5/1/2006 00:00 – 5/31/2007 23:59
5/8/2006 00:00 – 4/30/2007 23:59
5/8/2006 00:00 – 5/8/2007 23:59
5/8/2006 00:00 – 5/31/2007 23:59
6/1/2006 00:00 – 4/30/2007 23:59
6/1/2006 00:00 – 5/8/2007 23:59
6/1/2006 00:00 – 5/31/2007 23:59

Now, which functions gets used depends on the context in which it applies, so puting the functions in a stateful object doesn’t make sense, cause then I’d have actual line for line code resuse because the functions are used throughout different scenarios.  Right now, each function exists as a boolean function of a static class.  Each function has the following public signature:

public boolean IsWithinValidCalendarYearX(date value)

where X is a some sort of horrible naming convention particular to each function.

Clearly, this is a case for redesign.  These functions have been added over time, and I never went back and refactored the logic into something better.  Now its time to do so.  I could have a private function like:

private boolean IsWithingValidCalendarYear(date startDate, date endDate, date givenDate)

but there is still some work that has to be done in the public function to calculate what the start and end dates are, and an additional parameter to tell it which set of rules to apply, and that possibly makes the API more confusing, unless I used and enum as the 2nd parameter to the public function, which would reduce the confusion.  In the end, even though I’ve reduced the number of functions from 10 to 2, the same amount of code still exists, but with less duplicate lines.  I could really just have 1 public function, passing in the given date and the enum value and just do all the work right there, because I’m still going to have this mess of conditions and checks regardless of whether I have the private function call or not.  At least right now, my cyclomatic complexity is low for each method, but the API itself is ugly.

So where is the trade off?  Where does the line lie between ugly API and better code design?  Create a nicer API at the expense of more complex code, or leave a complex API at the benefit of less complex methods?  What are your thoughts and ideas around this?  How would you approach this?  The solution to this refactoring will also get applied to similar code smells in the logic that follow the same bad problem.



Comments

Jeffrey Palermo said:

Definitely introduce an interface.  You clearly have many different answers to the same question.  Of course, that's what I would do.  Each implementation of the interface would be of a different fiscal year structure.   For testing, then, you will have a place to stub a date evaluation.

And, of course, I'd recommend StructureMap for configuring the different implementations of the interface.
# May 8, 2006 4:11 PM

Matt Berther said:

This seems like it would be a good candidate for a Range pattern:

class DateRange
{
   private DateTime start;
   private DateTime end;
   
   public DateRange(DateTime start, DateTime end)
   {
       this.start = start;
       this.end = end;
   }
   
   public DateTime Start { get { return start; } }
   public DateTime End { get { return end; } }
   
   public bool Contains(DateTime value)
   {
       return value.CompareTo(start) >= 0 &&
           value.CompareTo(end) <= 0;
   }
}

After this, it would probably make sense to introduce a DateRangeFactory class to return instances that are appropriate to your particular business requirements.
# May 8, 2006 4:22 PM

MB said:

Sounds to me like a DateInterval class is needed. With the given details, my initial thought would be to have whatever class needs this date information to have a reference to a DateInterval class. The DateInterval class would have a function boolean .Includes(date givenDate). Then, just delegate it to your DateInterval and all is well.

My understanding is that this would fit the Strategy pattern for the pattern minded out there....But, I could be wrong. ;)

# May 8, 2006 4:25 PM

Tomas Restrepo said:

I'm not sure if I'm understanding here exactly the escenario, but here are my thoughts on it, anyway:

It seems to me like the problem here is that while all your methods calculate the same thing, they require different implementations. If you introduce interfaces as jpalermo says, you'd still have nine methods (actually, nine classes each with one method). Is this any better? It can be argued that it doesn't necessarily improve things, since it really complicates your calling code further.

After all, it is the calling method (i.e. the one that needs the date calculated) the one who has the context necessary to figure out what "calendar system" should be used to calculate the date. If you introduce interfaces, and maps and all that, then, hey, you made it harder to call (get implementation, call). Really, for this kind of thing, I'd say if you need more than one statement to calculate it, the api isn't very good (at least from a user pov).

However, implementing interfaces would make a lot of sense *if* you have more calendaring related functions around that might depend on the "system" you're using. If it is so, then creating an interface to group them, and create implementation classes for each system does make a lot of sense (and I'm guessing you might have a few of these around). These kind of things pop up all over the place back home in financial applications because we tend to need at least two different calendaring systems and deal with usually multiple sets of holidays depending on the region/city.
Heck, if you did this, and didn't want to bother creating calendar system objects all around, you could probably put them into a static class:

Calendars.FunkySystemA.Includes(date);

:)
# May 8, 2006 9:14 PM

Dave said:

.....I won't comment on your specific problem, but in terms of having complicated calling-code, or complicated API's I'd go compicated API every time.

I like Tomas' answer though.
# May 9, 2006 5:11 AM

John Woodard said:

Unless I'm mis-understanding the problem, I have to totally disagree with Dave.  If something is defined in an API, it is by definition something someone else is going to have to use, even if that someone else is yourself later on.  I would say simplify the API to the point that any decision that at best can be right is removed from the call.

For example, if the RIGHT call goes from 5/8 to 5/8, don't give the caller a start and end date that they can only call one way to get the right answer, give them something that already knows what the right way is for the context.

Besides, since which function gets used depends on the context of what's going on, isn't this a classic application of a Strategy or State pattern?
# May 9, 2006 6:54 AM

Raymond Lewallen said:

Jeffery, testing is not my issue.  Can you explain how implementing and interface would simply the API, or is it your suggestion that simplifying the API would not be favored at the expense of a more complex method?
# May 9, 2006 8:32 AM

Raymond Lewallen said:

Tomas, there is certainly a mix of logic required from both the API and the caller.  The caller must understand the domain and context of the problem in order to make the proper call, but the details of how that logic is applied to the given date is the responsibility of the API.  I can reduce the number of exposed methods via the API, but I'm never going to be able to remove that callers decision from their responsibility.  Maybe I'm just too picky.
# May 9, 2006 8:36 AM

Raymond Lewallen said:

To a certain degree, I think my question is being misunderstood.  How to implement a class to do the calculation, whether DateRange, DateInterval, Calendar etc it not necessarly the root of the problem.  The real problem lies in making the API easier and cleaner.  Even if I did introduce a DateRange class, I would still not expose it via the API, because then I would be removing the abstraction of the logic from the business layer and delegate that responsibility to the caller, making them figure out what the date range should be.  That shouldn't be the case with this type of logic.  The caller should just have to pass a given date and pick a way to process it, whether via choosing which method to call or by passing an additional parameter.  This is the only logic that should be the responsiblity of the caller, right?  This takes a back to the original question.  Should we favor the multiple methods exposed via the API in return for simple methods, or should we favor a more simple API, with a single function, at the expense of a complicated method.

I tend to lead towards the simple API, as simple and easy to use APIs are a core fundamental of API design, and this is where I disagree with Dave.  But, because this is a rare opportunity to refactor this type of code and make breaking changes if need be, I'm gathering the opinions of the rest of you so that I don't have to revisit something like this again down the road.  Like I said, I'm never going to be able to remove that decision on which call to make from the caller, but is there a way to make the call eaiser, which in my mind is creating a single exposed function with perhaps a 2nd parameter?  That makes the API code more complex and more difficult to debug, so, again, there is the underlying question (that I've already answered unto which way I'm leaning): single public method with complex logic versus multiple methods with simplified logic.  I lean towards simple API exposure, otherwise I probably wouldn't be having this discussion at all.

Maybe I just have too much time on my hands and think too hard about trivial things :)
# May 9, 2006 8:41 AM

Tomas Restrepo said:

Raymond, actually, my point was precisely in agreement with what you're saying: I'd go for the simpler API to call here (and I try to do that everytime). I also completely agree with you (that was part of the point I was trying to make, perhaps I didn't explain it very clearly) that the caller will always bear some of the responsability.

My example was just trying to combine both approaches (simple API, separate implementations, ease of replacing/adding implementations).
# May 9, 2006 9:32 AM

Jeffrey Palermo said:

Thanks for the clarification, Raymond.  I was thinking of the implementation with the API guts.  As far as the interface to the calling code, you need a way to specify the type of Date validation.  I'd imagine your API still should be an interface.  Then your default implementation could take an Enum in the constructor to define what kind of year you are using, and then your one method could do the work.

If you can define an enum with the different types of years you need to use, this might work.
# May 9, 2006 11:06 AM

Jeffrey Palermo said:

To go further, if you can give the types of year a unique name, then you can label each implementation with this name and use StructureMap to dynamically locate the one you want.

Please tell me if I'm not communicating this effectively.  In my mind, this implementation would be dirt simple and easy.
# May 9, 2006 11:08 AM

Raymond Lewallen said:

Jeff, crystal clear, as that has been the way I have been leaning: single method with an enum to specify the kind of year type calculation to use.  This adds complexity to the public method, but reduces the complexity of the API, which is really the goal of any API: simple to use and understand.  At this point, do to the size of my codebase, I'm sure that introducing StructureMap into the framework this far into the cycle is feasible and has enough trade-off value.  I've never used StructureMap, so I could be wrong.
# May 9, 2006 11:43 AM

Jeffrey Palermo said:

Great.  Glad I could help.  In fact, Jeremy just made the 1.0 release of StructureMap.  It's dirt simple and light-weight, but if you need help in setting it up (which is easy), just email me or Jeremy.
# May 9, 2006 11:48 AM

Raymond Lewallen said:

Oops.  I meant that I'm NOT sure that introducing StructureMap is feasible.  I've looked at it from time to time, but have never really just sat down and dedicate the time to it that I need to.  These next few weeks may give me that opportunity.

I'll be sure to let you guys know when I need help with it.
# May 9, 2006 11:53 AM

Raymond Lewallen [MVP] said:

Maybe you live in a cubicle.&amp;nbsp; You love your privacy, your
personal space, your segregation from...
# May 9, 2006 12:18 PM

TobinTitus said:

I "smell" the need to use waterfall!!  CMMI FTW!
# May 10, 2006 5:22 PM

virtualblackfox said:

SomeObject.IsWithinValidCalendarYear(someDate, StartCalendarYear.BeginingOfTheMonth, EndCalendarYear.EndOfTheMonth);

enum StartCalendarYear
{
   BeginingOfTheMonth,
   BeginingOfNextMonth,
   DirectDate
}

enum EndCalendarYear
{
   EndOfTheMonth,
   EndOfPreviousMonth,
   DirectDate
}

Maybe a little long to write, but easy to use if Intellisense is activated in VS and easy to understand.
# May 17, 2006 3:31 AM

Raymond Lewallen said:

VirtualBlackFox,

Your code example is ultimately similiar to how the public API would look if that route were taken, but you missed the point, along with some of the others.  How the code should be written is not the issue.  The issue isn't a coding issue so much as its a usability issue.  The issue is the API exposure, ease of use and delegation of process.  The overall discussion I was attempting to spur, unsuccessfully, is where the general splitting of logic should occur in a situation where you have this many similar routines.  Who should do the work, and how you would better expose that work via the API, and what expenses are you willing to incur in the underlying code to do so.  While your code example is a good example of how a particular solution could be done, it does nothing to explain why you would change the API in that fasion, and what the pros and cons are of the approach, which is what I was trying to get out of people.
# May 17, 2006 3:59 PM

Just a Thought said:

Keep everything as is but pull them all under the same umbrella to be called as follows:

'Validates 5/1/2006 00:00 – 4/30/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation1(date givenDate)

'Validates 5/1/2006 00:00 – 5/8/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation2(date givenDate)

'Validates 5/1/2006 00:00 – 5/31/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation3(date givenDate)

'Validates 5/8/2006 00:00 – 4/30/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation4(date givenDate)

'Validates 5/8/2006 00:00 – 5/8/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation5(date givenDate)

'Validates 5/8/2006 00:00 – 5/31/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation6(date givenDate)

'Validates 6/1/2006 00:00 – 4/30/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation7(date givenDate)

'Validates 6/1/2006 00:00 – 5/8/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation8(date givenDate)

'Validates 6/1/2006 00:00 – 5/31/2007 23:59
IsWithinValidCalendarYear.DescriptiveNameForValidation9(date givenDate)

Using a very descriptive name for each of the various “Year” calculation will help keep you and your teammates from pulling your hair out. Keeping each of these methods within their own class will allow you to reuse code and still keep the calling schema simple.
# May 19, 2006 2:11 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Raymond Lewallen

Working primarily in the public sector during his career, Raymond has designed and built several high profile enterprise level applications for all levels of the government. Raymond now works as a solutions architect for EMC. Raymond is an agile coach, Microsoft MVP C# and also president of the Oklahoma City Developers Group and Oklahoma Agile Developers Group. Raymond spends a lot of his time learning and teaching such things as Test Driven Development, Domain Driven Design, Design Patterns and Extreme Programming practices and principles, to name a few. Raymond is also an advocate of Alt.Net. Raymond is primarily a framework guy, so don't ask him anything about UI :) Check out Devlicio.us!