Los Techies : Blogs about software and anything tech!

Refactoring Day 30 : Return ASAP


This topic actually came up during the Remove Arrowhead Antipattern refactoring. The refactoring introduces this as a side effect to remove the arrowhead. To eliminate the arrowhead you return as soon as possible.

   1: public class Order
   2: {
   3:     public Customer Customer { get; private set; }
   4:  
   5:     public decimal CalculateOrder(Customer customer, IEnumerable<Product> products, decimal discounts)
   6:     {
   7:         Customer = customer;
   8:         decimal orderTotal = 0m;
   9:  
  10:         if (products.Count() > 0)
  11:         {
  12:             orderTotal = products.Sum(p => p.Price);
  13:             if (discounts > 0)
  14:             {
  15:                 orderTotal -= discounts;
  16:             }
  17:         }
  18:  
  19:         return orderTotal;
  20:     }
  21: }

The idea is that as soon as you know what needs to be done and you have all the required information, you should exit the method as soon as possible and not continue along.

   1: public class Order
   2: {
   3:     public Customer Customer { get; private set; }
   4:  
   5:     public decimal CalculateOrder(Customer customer, IEnumerable<Product> products, decimal discounts)
   6:     {
   7:         if (products.Count() == 0)
   8:             return 0;
   9:  
  10:         Customer = customer;
  11:         decimal orderTotal = products.Sum(p => p.Price);
  12:  
  13:         if (discounts == 0)
  14:             return orderTotal;
  15:  
  16:         orderTotal -= discounts;
  17:  
  18:         return orderTotal;
  19:     }
  20: }

This is part of the 31 Days of Refactoring series. For a full list of Refactorings please see the original introductory post.

Kick It on DotNetKicks.com
Posted Aug 28 2009, 12:38 PM by schambers

Comments

Jordi wrote re: Refactoring Day 30 : Return ASAP
on 08-30-2009 11:23 AM

Why?

(and again the bahavior in the two examples is different, because the Customer field may not be updated in the second example)

Why not do this?:

Customer = customer; // assuming the first example was the correct one

return products.Sum(p => p.Price) - discounts;

Pavan Kulkarni wrote re: Refactoring Day 30 : Return ASAP
on 08-31-2009 1:20 AM

Sean,

Great job here! I've been following your refactoring series all along. However, I don't agree to this particular refactoring method. Pre-mature returns, I've been told, should never be encouraged.

Udit Handa wrote re: Refactoring Day 30 : Return ASAP
on 08-31-2009 5:04 AM

Even I disagree on this. What if we are doing begin and end of function logging. We have to write the same code everywhere we return.

on 08-31-2009 11:56 AM

Agile/ALT.NET/Software Design A guide into OR/M implementation challenges: The Session Level Cache Build Your Own Data Access Layer: Lazy Loading A guide into OR/M implementation challenges: Laxy Loading Building Your Own Data Access Layer: Executing

Richard Dingwall wrote re: Refactoring Day 30 : Return ASAP
on 09-01-2009 5:10 AM

Arrowheads are one of the most common smells I see in legacy code. Replacing huge nested IFs with guard clauses and early bail-outs is always one of the first things I do, and it works wonders to simplify long functions down so they are ready for extract method etc.

Udit: I would do begin and end of function logging as in the caller or with a using() block.

mbrown77@gmail.com wrote re: Refactoring Day 30 : Return ASAP
on 09-01-2009 11:04 AM

@Udit: A Try Finally works great if you're doing Pre/Post Processing.

colinjack wrote re: Refactoring Day 30 : Return ASAP
on 09-02-2009 3:31 AM

To me thats introducing a guard.

Jake Tummond wrote re: Refactoring Day 30 : Return ASAP
on 09-20-2009 11:01 PM

Enjoying the series, although I seem to be reading from end to beginning.

Why make this function impure by passing a Customer object?  I would extract everything but the assignment into a pure static function.

Second, it seems some corner cases are uncovered?  What happens if discount > orderTotal?  Do you send customer some money?  Maybe it's an exception?

Finally, I think Sum will return zero for an empty list.

I would probably rewrite as:

{

 decimal orderTotal = products.Sum(p => p.Price);

 return Math.Max(0, orderTotal - discount);

}

Jake Tummond wrote re: Refactoring Day 30 : Return ASAP
on 09-20-2009 11:04 PM

Also, FWIW, I hate reading code with early returns.  I worked with a fellow who made it his mission to refactor deeply nested code this way.  I found it impossible to read and since he left, I've been slowly doing it the right way.  If your code is deeply nested, I don't think early returns are the way to fix it.  It is better to study the problem an fix it by modularizing.

31 Days of Refactoring « Vincent Leung's .NET Tech Clips wrote 31 Days of Refactoring &laquo; Vincent Leung&#039;s .NET Tech Clips
on 10-28-2009 9:28 AM

Pingback from  31 Days of Refactoring « Vincent Leung's .NET Tech Clips

张荣华 wrote 31天重构指南之三十:尽快返回
on 11-06-2009 3:35 AM

今天要说的重构是来自于“分解复杂判断”的扩展,当应用分解复杂判断时,我们总是需要尽可能快的返回。 1: public class Order 2: { 3: public Customer Custo...

PetterLiu wrote 31 Days of Refactoring
on 11-27-2009 4:02 AM

Refactoring Day 1 : Encapsulate Collection Refactoring Day 2 : Move Method Refactoring Day 3 : Pull ...

PetterLiu wrote 31 Days of Refactoring
on 11-27-2009 4:04 AM

Refactoring Day 1 : Encapsulate Collection Refactoring Day 2 : Move Method Refactoring Day 3 : Pull ...

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