Monday, December 27, 2010

#03 - How CSharp design patterns and refactoring differ from other languages - LINQ

This post continuing the post "How CSharp design patterns and refactoring differ from other languages - Part 01" and is based on Martin Fowler's "Refactoring" book.

Now here comes the first major difference between Java and CSharp
The next refactoring step is to extract Customer.GetTotalCharge() out of the loop of the enumerated rentals.

Making it the naive way it will look as folows:
        public double GetTotalCharge()
{
double result = 0;
foreach (var rental in _rentals)
result += rental.GetCharge();

return result;
}
But it could look much more elegant this way:
        public double GetTotalCharge()
{
return _rentals.Sum((Rental r) => r.GetCharge());
}
The only non-intutive part was that you have to first add "using System.Linq;" statement.

I'm not getting into how to work with LINQ, Anonymous methods and Lambbda Expressions, the same way I'm not getting into "Why refactoring".
I assume you have much better resources for that than this set of posts.

now that the extracted method became a one-liner (62 charaters long including the indentation) one might want to consider making it an inline statement.
I prefer it as a separate method for testing purposes.

* Actually we can shorten it to: "return _rentals.Sum((r) => r.GetCharge());"
As the type "Rental" is implied from _rentals.

Doing so for GetCharge and GetFrequentRenterPoints and - TADA!

Now the code for class Customer looks this way

using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Refactoring
{
public class Customer
{
public string Name { get; private set; }
List _rentals = new List();

public Customer(string name)
{
Name = name;
}

public void AddRental(Rental rental)
{
_rentals.Add(rental);
}

public string Statement()
{
IEnumerable rentals = _rentals;
string result = "Rental Record for " + Name + "\n";

foreach (Rental each in rentals)
{
// show figures for this rental
result += "\t" + each.Movie.Title + "\t" + each.GetCharge() + "\n";
}

// add footer
result += "Amount owed is " + GetTotalCharge() + "\n";
result += "You earned " + GetFrequentRenterPoints() + " frequent renter points";

return result;
}

public double GetTotalCharge()
{
return _rentals.Sum((r) => r.GetCharge());
}
public double GetFrequentRenterPoints()
{
return _rentals.Sum((r) => r.GetFrequentRentalPoints());
}
}
}
Hope you find it useful

Dekel

Thursday, December 23, 2010

How CSharp design patterns and refactoring differ from other languages - Part 02

This post continuing the post "How CSharp design patterns and refactoring differ from other languages - Part 01" and is based on Martin Fowler's "Refactoring" book.

For the refactoring explanation, please read the book, Marting Fowler explains it much better than I'll ever could. I'll concentrate of how it is done in CSharp.

So the first refactoring action in the book is "Extract Method" ( select the code from "double thidAmount = 0" till the end of the switch block, Menu --> Refactor --> Extract Method )
The automatic Extract-Method saves you the human errors such as double Vs. int, Yet - You always needed to test it and verify that the automation did what you expected it to do.

Next - renaming "each" to "aRental" (Not that I like this naming convention, but it is better than "each" and is is soon going away) and "thisAmount" --> "result"

And Moving Customer.AmountFor(Rental aRental) to Rental.GetCharge()
* You are missing the middle step - since I struggled with blogger editor html Vs WYSIWYG modes and lost some of the versions and my patience
---
using System.Collections;
using System.Collections.Generic;

namespace Refactoring
{
public class Customer
{
public string Name { get; private set; }
List _rentals = new List();

public Customer(string name)
{
Name = name;
}

public void AddRental(Rental rental)
{
_rentals.Add(rental);
}

public string Statement()
{
double totalAmount = 0;
int FrequentRenterPoints = 0;
IEnumerable rentals = _rentals;
string result = "Rental Record for " + Name + "\n";

foreach (Rental each in rentals)
{
double thisAmount = each.GetCharge();
// Add frequent renter points
FrequentRenterPoints++;
if (each.Movie.PriceCode == Movie.NEW_RELEASE &&
each.DaysRented > 1)
FrequentRenterPoints++;

// show figures for this rental
result += "\t" + each.Movie.Title + "\t" + thisAmount + "\n";
totalAmount += thisAmount;
}

// add footer
result += "Amount owed is " + totalAmount + "\n";
result += "You earned " + FrequentRenterPoints + " frequent renter points";

return result;
}
}
}
---

namespace Refactoring
{
public class Rental
{
public Movie Movie { get; private set; }
public int DaysRented { get; private set; }

public Rental(Movie movie, int daysRented)
{
Movie = movie;
DaysRented = daysRented;
}

public double GetCharge()
{
double result = 0;

// Determine amount for each line
switch (Movie.PriceCode)
{
case Movie.REGULAR:
result += 2;
if (DaysRented > 2)
result += (DaysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += DaysRented * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (DaysRented > 3)
result += (DaysRented - 3) * 1.5;
break;
}

return result;
}
}
}
---

Tuesday, December 21, 2010

How CSharp design patterns and refactoring differ from other languages - Part 01

I'm reading Martin Fowler's "Refactoring" book, and recalling a course I had long ago about design patterns.

Now - these are extreamly useful tools, as you probably know, but...
When dealing with CSharp (and esspecially .Net 4.0) you have an extended set of tools that make some things so elegant it changes the way you can refactor and apply patterns.

The simplest example is "delegate" (and "anonymous delegate")
In the old-school of design patters we talk about classes and interfaces, while nowdays you can call the same method and pass it different worker delegate so easily, it changes the way you think about your code.

On the first chapter of Fowler's book (Where the examples are in Java) he shows extacting out a while statement that takes 6 lines of code, while using LINQ it would be a one-liner, and a readable one.

All of that said in order to explain that there should or at least could be a sub-version of both "Design Patterns" and "Refactoring" that is .Net specific and makes use of the great new features.

And now I need to get to work on getting the examples and translate them into CSharp.

So here is the initial code translated to CSharp
Note the different notation for class properties, Enumerations and const declerations
No deep change

---
namespace Refactoring
{
public class Movie
{
public const int CHILDRENS = 2;
public const int REGULAR = 0;
public const int NEW_RELEASE = 1;

public int PriceCode { get; set; }
public string Title { get; private set; }

public Movie(string title, int priceCode)
{
Title = title;
PriceCode = priceCode;
}
}
}
---
namespace Refactoring
{
public class Rental
{
public Movie Movie { get; private set; }
public int DaysRented { get; private set; }

public Rental(Movie movie, int daysRented)
{
Movie = movie;
DaysRented = daysRented;
}
}
}
---
using System.Collections;
using System.Collections.Generic;

namespace Refactoring
{
public class Customer
{
public string Name { get; private set; }
List _rentals = new List();

public Customer(string name)
{
Name = name;
}

public void AddRental(Rental rental)
{
_rentals.Add(rental);
}

public string Statement()
{
double totalAmount = 0;
int FrequentRenterPoints = 0;
IEnumerable rentals = _rentals;
string result = "Rental Record for " + Name + "\n";

foreach (Rental each in rentals)
{
double thisAmount = 0;

// Determine amount for each line
switch (each.Movie.PriceCode)
{
case Movie.REGULAR:
thisAmount += 2;
if (each.DaysRented > 2)
thisAmount += (each.DaysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
thisAmount += each.DaysRented * 3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if (each.DaysRented > 3)
thisAmount += (each.DaysRented - 3) * 1.5;
break;
}

// Add frequent renter points
FrequentRenterPoints++;
if (each.Movie.PriceCode == Movie.NEW_RELEASE &&
each.DaysRented > 1)
FrequentRenterPoints++;

// show figures for this rental
result += "\t" + each.Movie.Title + "\t" + thisAmount + "\n";
totalAmount += thisAmount;
}

// add footer
result += "Amount owed is " + totalAmount + "\n";
result += "You earned " + FrequentRenterPoints + " frequent renter points";

return result;
}
}
}
---

* Sorry for the missing indentation, you can paste it to VS and let it fix it

Let me know if you find this helpful.