he eXtreme .NET Team
- eXreme Eddie?Eddie has been programming for more than 20 years. He has done everything from mainframe work through embedded systems. Eddie was an early adopter of XP techniques and has been using XP in Java projects for the past couple of years. Eddie embodies the XP spirit.
- .NET Deepak?Deepak is a young, smart coder. He graduated last year but has been using the .NET Framework since it first went into beta. Deepak knows and loves .NET. Deepak represents all there is to love about .NET.
- Skeptic Sue?Sue has been in software development for 10 years. She has been mainly developing Windows software in C and C++. Sue was promoted in her previous job to project manager, which she hated. She left and joined this team so that she could get back into developing software. Sue carries the scars from many failed projects and doesn’t trust new ideas.
- Panic Pete?Pete is very bright and comes up with amazing solutions for problems. He is the clown of the team. Pete has been writing code for five years but has never finished a single project he started. Pete panics when the going gets tough and verbalizes this panic. When this happened at a previous job he quit.
- Customer Chris?Chris is the internal customer for the team. He works with marketing and the customer support team. Chris once tried to write some code in a training course. He didn’t enjoy it. Chris is a people person: He loves engaging in conversation.
Morning Meeting
Let’s join the team now as they hold a morning meeting to discuss their project.
Eddie: Good morning, everyone!
Team: Morning!
Eddie: I am really pleased with how things are going. How do you feel Chris?
Chris: Yes, it looks like you have gotten off to a good start.
Deepak: I am concerned about using the CourseCosts library; the code in there is pretty messy.
Pete: Panic!
Sue: Why? Did you write that library Pete?
Pete: (looking somewhat sheepish) Yes, kind of. I was involved in that project but I left the team before they shipped.
Chris: How does this impact me? The SportSpeak project allows them to start selling courses online and we were banking on reusing that library. Wasn’t that the plan?
Eddie: Sure that is the plan. What do you think Deepak?
Deepak: I think we should be fine, but if we are going to touch the code in there we will probably want to do some refactoring.
Chris: What’s that? Is it expensive? I thought we just needed to add the ability to cope with event sponsors injecting money into the event.
Eddie: Refactoring is changing the structure of the code without changing the behavior of the code.
Chris: OK, so you want to do what exactly?
Sue: I think it is a bit like doing spring cleaning on the code. Something Pete could do on his desk.
Pete: (Smiling) If you stopped leaving all your junk on my desk it would only be half as messy!
Everyone except Sue laughs for a moment before realizing that Sue has not taken this well and is glaring at Pete.
Chris: OK, well we have job to do here guys. Is there something that will make this code cleaning exercise any easier for you?
Eddie: In the last tool I used for a Java project we had refactoring tools in the IDE. I haven’t seen anything like that in Visual Studio .NET.
Deepak: I have heard Microsoft will put some in the next version.
Pete: Yeah I read that too. VS2005 will have refactoring tools. That looks cool!
Sue: Great, but until you smart alecs invent a time machine we are stuck with Visual Studio 2003.
Deepak: There are a couple of tools I’ve seen that plug-in to Visual Studio for doing refactoring.
Eddie: Oh, that’s interesting! Have you used them?
Deepak: I downloaded the trial of Resharper and I could show you some of what it does.
Pete: Cool!
Eddie: Well, since you worked on that project Pete, and Deepak has played with this Resharper tool, why don’t the two of you pair up this morning and see if it will help us move forward with working on the library?
Deepak and Pete look at each other and grin.
Sue: And no monkey business guys!
Pete: Would we?
Eddie: Um? yes most likely!
Chris: OK, so we’ll know this afternoon if this tool is worth buying?
Deepak: Yeah I reckon we should.
Eddie: Great! Let’s get to it then.
Exploring Resharper
Deepak and Pete take one look at Pete’s desk and decide to work at Deepak’ s desk instead. They load up the CourseCosts library.
Here’s a version of the library that includes all of the code examined in this article.
You can download a trial version of Resharper from JetBrains at http://www.jetbrains.com/resharper/.
Deepak: I’ve already installed Resharper on this machine.
Pete: I guessed that is why you have the extra Resharper menu on your Visual Studio menu bar.
Deepak: Sue is right, You can be a smart ass sometimes!
Pete grins but remains silent.
Deepak: So let’s see what this project consists of.
Pete: The files we’d be interested in are probably the CourseCost and CostItems classes.
Deepak: Oh! You do know something about this project, don’t you?
Pete: Like I said, kind of?
Deepak: Hey, there is even a test class in here with a whole test in it!
Pete: Yes it uses that text.xml file, I remember this. We have to copy that file to the root of our C: drive to get the test to work.
Deepak: And what exactly is the reason for that?
Pete: The CourseCost class works out the cost of a course for each attendee using the figures in the XML file. Open it up and I’ll explain it to you.
Deepak opens the text.xml file shown below.
3000 30 2500 2000 30
Pete: So the idea is there will be an entry for each location the company runs courses. Here we have only created one for the test: London. Then the cost items are listed as children of that entry. The MaxAttendees and Catering are kind of special cases.
Deepak: How so?
Pete: Well, the other costs are fixed costs for the course, The Catering is a cost per attendee. The MaxAttendees is not a cost at all, but the maximum number of attendees that venue can cater for.
Deepak: OK, makes sense so far. Let’s look at the code.
Deepak opens the CourseCost.cs file as shown in Listing 1.
Deepak: Yes, this is what I was talking about! This is a real mess.
Pete: PANIC!
Eddie: Is everything OK with you guys?
Deepak: Yeah we’re just looking at the messy code and?
Pete: PANIC!
Deepak: I’m sure we can fix it!
Pete: (Sounding a bit doubtful.) OK, show me what we can do.
Deepak: OK, let’s start with those public member variables. Do they really need to be public?
Pete: What, the costItems and the attendees?
Deepak: Yes, Resharper can show us where they are being used.
Pete: That’s cool! How do we do that?
Deepak: Move the mouse over the costItems variable name and right-click. Do you see the “Find Usages” item in the popup menu?
Pete: Yep.
Deepak: Click on it.
Pete does this and a dialog appears showing the places the variable is used (see Figure 1).
![]() | ? |
Figure 1. Find Usages Dialog: The figure shows the results of using the Find Usages dialog to find all occurrences of a variable. |
Deepak: As I thought, it is not used outside of this class. Let’s change it to a private variable. Check the attendees as well.
Pete: The same, I’ll change both of them.
Deepak: Great, now let’s look at that nasty big function GetCost.
Pete: OK, what’s wrong with it?
Deepak: I think it smells. It seems too large and is doing too much. We should take some of the functionality out into smaller methods.
Pete: OK, but don’t we pay the cost of method invocation? Isn’t that going to slow the code down?
Deepak: Is speed an issue for this component?
Pete: I don’t know. It might be.
Deepak: I think until we know it is an issue, I’d rather not worry about it.
Pete: OK, What shall we do about this big method then?
Deepak: We can start by extracting the code that calculates the total cost into its own method. Select the foreach loop that goes through the costItems, and right-click. Do you see the Refactor menu item at the bottom of the popup menu?
![]() | ? |
Figure 2. Extract Method: The figure shows the Extract Method dialog. |
Pete: Yes.
Deepak: Select that and then select Extract Method.
Pete does this and the Extract Method dialog box appears as shown in Figure 2.
Pete: OK, now what?
Deepak: How about changing the return value so it returns the totalcost and we’ll call the method?
Pete: TotalCost?
Deepak: Yes, that should do it. You can see the signature preview at the bottom of the dialog. I like that! Click OK.
The dialog before Pete clicks OK is shown in Figure 3.
![]() | ? |
Figure 3. Extract Method Dialog: The figure shows the Extract dialog used to extract the TotalCost method. |
Pete: Hey cool! It took the code out of the big function and made the call to it and everything!
Deepak: It’s nice huh?
Pete: Sure is, what’s next?
Deepak: Let’s do the same with the other foreach loop that extracts the cost items from the XML nodes.
Pete: OK, what shall we call this function?
Deepak: How about ExtractCostItems?
Pete: Great, look at that! A few clicks of the mouse and the code is starting to look like proper object-oriented code!
Pete grins, guessing this is just the tip of the iceberg. The code that Pete and Deepak are now looking at is shown in Listing 2.
Eddie: How are you guys doing?
Pete: Really good. It’s so easy to change the code using this Resharper tool.
Eddie: That’s good to hear but are you thinking about why you are changing the code? What is the task at hand?
Deepak: Oh!
Pete: Um… we have just been playing with what’s there. I haven’t really been thinking about the task. Um? what is the task?
Deepak: Something to do with handling a sponsor paying for some of the costs.
Eddie: Chris, can you help these guys understand what the CourseCosts library does?
Understanding the Task
Chris: Sure. We need to change it so it can account for having a sponsor pay for some of the costs. This reduces the amount that the course will cost to attendees.
Pete: Right. So we just need to add the sponsor payment to the XML document and then deduct that from the total cost before working out the cost per delegate?
Deepak: That sounds right. Chris, would you know if the cost was right if we gave you the numbers and results we get from the program?
Chris: Sure.
Deepak: OK, let’s do it.
Pete: I’ll change the XML file by adding a sponsor payment node.
Pete edits the XML file to look like this.
3000 30 2500 2000> 30 900
Deepak: Hold on. What are you doing? I think we should change the XML file format. It is getting very messy with all these special cases.
Pete: Why? I thought we should just do the simplest thing. That’s what Eddie keeps telling us. It’s easy if we just add another if statement to the bit where we get all the data for a location.
Deepak: I have always interpreted “do the simplest thing” as meaning in the code. So it’s the code that should be simple, not us!
Pete: You know you can be quite funny at times, and that wasn’t one of them.
Deepak laughs, partly from embarrassment and partly because he knows Pete is right. Deepak takes his job as seriously as Pete takes his fooling around.
Deepak: OK, let’s do it your way and then we can refactor the code to make it simpler.
Pete: Alright.
Pete grins and cracks his knuckles before tapping out the following changes to the ExtractCostItems method.
private decimal sponsorPayment; private void ExtractCostItems(XmlNodeList nodes) { XmlNodeList children = nodes[0].ChildNodes; foreach(XmlNode item in children) { if (item.Name == "MaxAttendees") { attendees = Convert.ToUInt32(item.InnerXml); } else if (item.Name == "SponsorPayment") { sponsorPayment = Convert.ToUInt32(item.InnerXml); } else { CostItems Item = new CostItems(item.Name, Convert.ToUInt32(item.InnerXml)); costItems.Add(Item); } } }
Deepak: You see what I mean about getting messy?
Pete: (grinning) It looks like some of the best code I’ve ever written!
Deepak: At times I can believe you are telling me the truth, this isn’t one of them!
Pete: Touche!
Pete carries on at the keyboard and makes the following changes to the TotalCost method.
private decimal TotalCost(decimal totalCost) { foreach(CostItems item in costItems) { if (item.Name == "Catering") { totalCost += item.Cost*attendees; } else totalCost += item.Cost; } totalCost -= sponsorPayment; return totalCost; }
Deepak: OK, we’ll need to change the test as well now. Run the test and make sure it’s broken.
Pete: Hey that’s the wrong way around isn’t it? Aren’t we supposed to break the test and then fix the code?
Deepak: Well it was your happy fingers on the keyboard plowing into the code first! Let me drive for a bit.
Pete hands Deepak his keyboard.
Pete: OK, go ahead.
Deepak runs the test in NUnit GUI and it passes.
Pete: Hey great! See I told you it was easy.
Deepak: But that doesn’t make any sense. There is no way the test should pass. What’s going on here?
Pete: Hold on! We forgot to copy the XML file to the root of the C: drive.
Deepak: Man this is a mess.
Deepak copies the test.xml file to the C: drive and runs the test again. This time the test fails.
Pete: So how do we make the test pass now?
Deepak: We’re doing this the wrong way around but now we have to change the test to reflect the changes we just made to the code.
Deepak changes the expected result in the test so it now looks like this.
[Test] public void GetCourseCost() { CourseCost cCost = new CourseCost(@"C:Test.xml"); decimal cost = cCost.GetCost("London"); Assert.AreEqual(500, cost, "Cost is not correct"); }
They run the test again, this time it passes.
Deepak: OK, we’re back on track now.
Pete: Hold on, I’ve just thought of something.
Deepak: What?
Pete: Chris? Can a course have more than one sponsor?
Chris: Yes and yes!
Deepak: OK!
Pete: Now we really need some help.
Deepak: I knew this mess was going to cause trouble. We should get that XML file into shape first. Then we can get the code to work.
Pete: Huh? That doesn’t sound right.
Deepak:Well do you have any better suggestions?
Pete: Um? nope.
Deepak: Right, so lets do this and then we can always change it later.
Deepak edits the XML file so that it contains a hierarchy of the costs as shown.
30 3000 2500 2000 30 900
Pete: That looks good.
Deepak: Well it’s more descriptive of what the costs actually are.
Pete: I get it. We’ll work with the categories of costs and then use the total from each category to calculate the total cost!
Deepak: Right. Actually, categories is a good name to use.
In the ExtractCostItems method Deepak renames the XmlNodeList variable from children to categories.
Pete: Great, you should also rename the XmlNode item variable in the foreach loop. Call that “category.”
Deepak: Well spotted.
Pete: You should also change the costItems array to be called fixedCostItems, capital C, capital I.
Deepak: Good thinking. I guess we should also change the sponsorPayments to an ArrayList.
Pete: Yeah, and add a new ArrayList called variableCostItems.
Deepak: We should initialize them in the constructor like this.
public CourseCost(string Filename) { srcFile = Filename; fixedCostItems = new ArrayList(); variableCostItems = new ArrayList() ; sponsorPayments = new ArrayList(); }
Pete: Sweet! Hey, can I drive for a bit? I think I know what we need to do in the ExtractCostItems method.
Adding the Finishing Touches
Deepak hands Pete the keyboard.
Deepak: Sure, go ahead.
Pete changes the if-else conditions in the foreach loop to a switch statement as shown in Listing 3.
Deepak: Oh no it’s getting worse, not better!
Pete: Relax man. This is exactly what I planned. Watch this. . .
Pete uses the Extract Method on the code inside the fixed costs case of the switch statement. He calls the new method GetCategoryCosts. It looks like this.
private void GetCategoryCosts(XmlNode category) { foreach(XmlNode cost in category.ChildNodes ) { CostItems Item = new CostItems(cost.Name, Convert.ToUInt32(cost.InnerXml)); fixedCostItems.Add(Item); } }
Deepak: OK, I think I see what you are doing.
Pete: Good, but this Resharper tool wouldn’t let me pass the ArrayList into the extracted method. Stupid thing, now I have to actually write some more code!
Pete changes the method to use an ArrayList passed as a parameter rather than as the fixedCostItems collection.
private void GetCategoryCosts(XmlNode category, ArrayList categoryList) { foreach(XmlNode cost in category.ChildNodes ) { CostItems Item = new CostItems(cost.Name, Convert.ToUInt32(cost.InnerXml)); categoryList.Add(Item); } }
He then changes the call in the FixedCosts case to pass the fixedCostItems variable into the newly extracted method.
case "FixedCosts": GetCategoryCosts(category, fixedCostItems); break;
Deepak: Hey pretty smart, now we can do the same with the other case statements!
Pete: Yes indeed.
Pete edits the rest of the cases in the ExtractCostItems method so that it now looks like this:.
private void ExtractCostItems(XmlNodeList nodes) { XmlNodeList categories = nodes[0].ChildNodes; foreach(XmlNode category in categories) { switch (category.Name) { case "MaxAttendees": attendees = Convert.ToUInt32(category.InnerXml); break ; case "FixedCosts": GetCategoryCosts(category, fixedCostItems); break; case "VariableCosts": GetCategoryCosts(category, variableCostItems); break ; case "Sponsors": GetCategoryCosts(category, sponsorPayments); break; default: break; } } }
Deepak: Super cool! That is looking much better. We’ll need to change the TotalCost method now to use the values in the collections.
Pete edits the TotalCost method to use the collections as shown below:
private decimal TotalCost(decimal totalCost) { foreach(CostItems item in fixedCostItems) { totalCost += item.Cost; } foreach(CostItems item in variableCostItems) { totalCost += item.Cost*attendees; } foreach(CostItems item in sponsorPayments) { totalCost -= item.Cost; } return totalCost; }
Deepak: OK, does the test still run?
Pete runs the test in NUnit and it passes.
Deepak: Looking good!
Pete: So are we done?
Deepak: I think so. We’ve added the functionality that Chris asked for.
Pete: But I’m getting the hang of this now. I reckon there are more places we could tidy up the code here.
Deepak: We probably could but the code is doing what we want now. I think we should leave it until we need to add another new feature to this class library.
End of Day Wrap Up
At the end of the day the team gathers to discuss how they are doing using Resharper.
Eddie: So do you guys reckon Resharper helped at all?
Pete: I’m not sure. It seems to have a lot of other cool features. It brings up warnings as you’re typing the code.
Deepak: I think it would be more useful in a big project when you want to zip around the code and find out where methods are called from.
Pete: The refactoring tools are really only a small subset of the Resharper toolkit and if we only buy it for that, then it might not be the best use of our money.
Eddie: So as we extend this project you think it will become more valuable?
Deepak: Yes I think that as we start to create more dependencies between classes we’ll find more use out of the toolkit.
Chris: OK, it sounds like we ought to investigate if we can get licenses for you guys.
Deepak: Well, we can download and use the trial for a month and then make a decision.
Chris: Why don’t you do that? It will probably take that long to raise a purchase order anyway.
Eddie: Great work team! Who’s up for pizza and a beer?
Sue: (sighing) You boys!