var blog = new ThoughtStream(me); RSS 2.0
 Tuesday, March 11, 2008

As a kid, I was never part of the boy-scouts or anything; but my family and I went camping a lot, and I went camping with my youth group on several occasions. I remember hearing my parents and the various youth leaders talking about we should always leave the camp site cleaner than we found it. I always thought this was annoying - why should I clean up someone else's mess? If I clean up my own mess, isn't that good enough?

Yesterday, while helping a coworker fix some bugs in an application that I wrote around a year ago, I was suggesting ways to improve various parts of the code; move this property to a parameter of that method, make this method private and only call it from here in the owning class, and items as simple as making an if-then statement easier to read.

After a few of these suggestions, he asked me if I always clean up the code that I'm working with, even if the bug is not directly related to the code that we are cleaning up. My answer was emphatically, "yes". If I'm reading code, trying to find a bug and I'm having a hard time understand what's going on with the code, then it becomes much more difficult to find the actual bug. Even if this code does not end up being part of the bug I was looking for, by cleaning up the code I am making it more likely that I will be able to understand what this code is doing the next time I have to look at it.

Here's my basic perspective that drives all of this: if you have a hard time reading the code and seeing what it is doing, chances are, you or someone you know will have to debug that code at some point. I don't want to debug hard to read code - that's annoying, at best. I want to debug code that is easy to read and easy to understand. And I certainly don't want to make any of my coworkers debug hard to read code. I try not to torture coworkers like that. So, if my motivation is to not debug hard to read code, then doesn't it make sense that I would want to clean up that ugly code? It makes sense to me...

What does this really come down to, then? Two things:

  1. Leave the code cleaner than when you arrived, by
  2. Micro-refactoring - make that one line of code easier to read

 

 

(By the way - there's no such thing as "micro-refactoring". Refactoring, by definition, is exactly what I described above. Stop trying to change the architecture and learn to change that one line of ugly code. By doing this, you'll find that the architecture does change, because you clean up more than you realize and change become natural.)

Tuesday, March 11, 2008 7:51:49 AM (Central Standard Time, UTC-06:00)  #    Comments [1]. Trackback 
Tags: Agile | General | Refactoring

 Monday, March 10, 2008

Have you ever:

  • had a problem that you were having a hard time solving?
  • been in need of a design idea for a particular situation, and you don't know where to start?
  • solved a problem that was nagging you for a while?
  • come up with a good design for a common situation?
  • written some code that you wanted to keep around, to remind yourself how you did something?
  • wanted to find some code examples on how to do something with a specific technology?
  • wanted to know how to do something for a specific project?
  • wanted to share your knowledge on how to use a specific technology a specific way?
  • wanted to learn how to use a specific feature of a project?
  • wanted the world to know your opinion of a piece of software or technology, be it good or bad?

If you can answer "yes" to any one of these questions - you should be blogging.

If you can answer "yes" to more than one of these questions and you are not blogging, then shame on you! Start blogging today!

Don't think your opinion matters, or that you have anything worth saying? Stop fooling yourself. If you write code, you have opinions and preferences. If you have opinions and preferences, they are worth sharing. It's not possible to write code without opinions. Software development is not a mechanical process like building a house or a car - you can't sick a robot on a keyboard and write a functional piece of software.

The worst case scenario: If you post code examples on issues that you have solved, you will have a history of code you have written and issues you have solved. You'll be able to go back to this history and re-use existing knowledge, rather than having to think through the problem again.

The best case scenario: If you post your code examples, your thoughts on software development, your opinions and preferences; chances are that someone else in this wide world of ours has the same opinion or has had the same issues and will find the information you provide useful.

Why should you blog? Because you're a person with ideas worth listening to.

...

Get started now. Register a domain name and buy some web hosting so you can have a blog that is accessible to the world. I use dasBlog and WebHost4Life. There are thousands of options out there - find the one that works for you.

Monday, March 10, 2008 10:37:30 AM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: General

 Saturday, March 08, 2008

After many years of being a loyal RSSBandit user, I've jumped ships and started using Outlook 2007 to manage all of my RSS feeds. My only reason is that I'm already using Outlook for so many other functions, and now that we are implementing SharePoint, that list of reasons is growing.

One of the things I did not like about Outlooks' default RSS capabilities, was not being able to see a quick, concise list of the feeds / posts that I have not yet read. Fortunately, there is an easy solution to this: Search Folders.

To create a quick, easy way to view all of the unread posts in your feed list, follow these simple steps:

  1. Right click on "Search Folders" in the Outlook tree and select "New Search Folder..."
    image
  2. On the screen that pops up, choose "Create a custom Search Folder" at the bottom of the list:
    image
  3. Click the "Choose" button for specifying criteria and name your folder
    image
  4. Click "Browse" to select the folders that you want to search. Unsleect the root "Mailbox" folder, and select the "RSS Feeds" sub-folder. Ensure that "Search subfolders" is selected.
      image
  5. Click OK to close the "Select Folder(s)" screen. Now click the "Criteria" button, and under the "More Choises" tab, select "Only items that are: ", "unread".
    image
  6. Click OK to close the criteria screen, click OK to close the Custom Search Folder screen, and Click OK to close the New Search Folder screen. You now have an "Unread RSS" folder in your Search Folders. Drag this item into your "Favorite Folder" list and you are done! 
    image

Now your unread RSS feeds are only a button click away! And the great part about this simple solution is that you can create some very specific RSS searches, in additional custom Search Folders. Just follow this process again, only selecting the specific folders that you want.

Saturday, March 08, 2008 9:15:29 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags:

I just can't help myself... I had to flesh out the Account Transfer behavior from my previous post. I was interested in two things:

  1. Adding a second set of Acceptance Criteria for a valid transfer
  2. Implementing the crazy looking "account.Transfer(ammount).To(account)" syntax

Expanded Acceptance Criteria

Existing User Story:

As an account holder,
I want to transfer money between accounts
So that I can avoid overdraft fees

Acceptance Criteria:

  1. Given a transfer between accounts,
    When the requested transfer amount is greater than the originating account's balance
    Then the transfer should fail and the balance of the originating and receiving accounts should not change
  2.  

  3. Given a transfer between accounts,
    When the requested transfer amount is less than the originating account's balance
    Then the transfer is successful, the originating account is debited the transfer amount and the receiving amount is credited the transfer amount

Implementing The Transfer.To Syntax

This was the fun part, really - seeing if I could implement the API that I wanted to see. It turned out to be significantly easier than I had thought it would be.

I started with the Account object, and added the "Transfer" method. I knew that this method needed to return an object so that I could have the "To" method accept an Account object. A few moments of thinking this through and I decided to go with an object called TransferCriteria. This would let me collect all of the information that I need, about the transfer.

public TransferCriteria Transfer(double ammount)
{
    TransferCriteria criteria = new TransferCriteria(this, ammount);
    return criteria;
}

The end-goal of this syntax model is to return an AccountTransfer object, directly. So, the TransferCriteria.To method then creates an AccountTransfer object, using the TransferCriteria.

public AccountTransfer To(Account receivingAccount)
{
    ReceivingAccount = receivingAccount;
    return new AccountTransfer(this);
}


In the end, I have an AccountTransfer object that contains all of the information I need - the originating account, the receiving account, and the transfer amount. With that in place, the rest of the code is simple.

Behavior Specification

AccountTransferSpecifications.cs

using AccountSample.Domain;
using NUnit.Framework;
using SpecUnit;
 
namespace AccountSample.Specifications
{
 
    [TestFixture]
    [Concern("Account Transfers")]
    public class When_The_Requested_Transfer_Amount_Is_Greater_Than_The_Originating_Account_Balance : ContextSpecification
    {
 
        #region Context
 
        private const double originatingAccountBalance = 100;
        private const double receivingAccountBalance = 0;
        private const double requestedTransferAmmount = 125.95;
        private Account originatingAccount;
        private Account receivingAccount;
        private AccountTransfer accountTransfer;
 
        protected override void Context()
        {
            originatingAccount = new Account(originatingAccountBalance);
            receivingAccount = new Account(receivingAccountBalance);
 
            accountTransfer = originatingAccount.Transfer(requestedTransferAmmount).To(receivingAccount);
        }
 
        #endregion
 
        #region Observations
 
        [Test]
        [Observation]
        public void The_Transfer_Should_Fail()
        {
            accountTransfer.Status.ShouldEqual(TransferStatus.Failed);
        }
 
        [Test]
        [Observation]
        public void The_Originating_Account_Balance_Should_Not_Be_Changed()
        {
            originatingAccount.Balance.ShouldEqual(originatingAccountBalance);
        }
 
        [Test]
        [Observation]
        public void The_Receiving_Account_Balance_Should_Not_Be_Changed()
        {
            receivingAccount.Balance.ShouldEqual(receivingAccountBalance);
        }
 
        #endregion
 
    }
 
    [TestFixture]
    [Concern("Account Transfers")]
    public class When_The_Requested_Transfer_Amount_is_Less_Than_The_Originating_Account_Balance: ContextSpecification
    {
 
        #region Context
 
        private const double originatingAccountBalance = 100;
        private const double receivingAccountBalance = 0;
        private const double requestedTransferAmmount = 25;
        private Account originatingAccount;
        private Account receivingAccount;
        private AccountTransfer accountTransfer;
 
        protected override void Context()
        {
            originatingAccount = new Account(originatingAccountBalance);
            receivingAccount = new Account(receivingAccountBalance);
 
            accountTransfer = originatingAccount.Transfer(requestedTransferAmmount).To(receivingAccount);
        }
    
        #endregion
 
        #region Observations
 
        [Test]
        [Observation]
        public void The_Transfer_Is_Successful()
        {
            accountTransfer.Status.ShouldEqual(TransferStatus.Success);
        }
 
        [Test]
        [Observation]
        public void The_Originating_Account_Is_Debited_The_Transfer_Amount()
        {
            const double newBalance = originatingAccountBalance - requestedTransferAmmount;
            originatingAccount.Balance.ShouldEqual(newBalance);
        }
 
        [Test]
        [Observation]
        public void The_Receiving_Account_Is_Credited_The_Transfer_Amount()
        {
            const double newBalance = receivingAccountBalance + requestedTransferAmmount;
            receivingAccount.Balance.ShouldEqual(newBalance);
        }
 
        #endregion
 
    }
 
}

Behavior Implementation

Account.cs

namespace AccountSample.Domain
{
 
    public class Account
    {
 
        #region Properties
 
        public double Balance { get; private set; }
 
        #endregion
 
        #region Constructor
 
        public Account(double currentAccountBalance)
        {
            Balance = currentAccountBalance;
        }
 
        #endregion
 
        #region Methods
 
        public TransferCriteria Transfer(double ammount)
        {
            TransferCriteria criteria = new TransferCriteria(this, ammount);
            return criteria;
        }
 
        public void Debit(double ammount)
        {
            Balance -= ammount;
        }
 
        public void Credit(double ammount)
        {
            Balance += ammount;
        }
 
        #endregion
 
    }
 
}

 

AccountTransfer.cs

namespace AccountSample.Domain
{
 
    public class AccountTransfer
    {
 
        #region Vars
 
        private TransferStatus _status = TransferStatus.None;
 
        #endregion
 
        #region Properties
 
        public TransferStatus Status
        {
            get { return _status; }
            private set { _status = value; }
        }
 
        public double TransferAmmount { get; private set; }
 
        public Account OriginatingAccount { get; private set; }
 
        public Account ReceivingAccount { get; private set; }
 
        #endregion
 
        #region Constructor
 
        public AccountTransfer(TransferCriteria criteria)
        {
            OriginatingAccount = criteria.OriginatingAccount;
            ReceivingAccount = criteria.ReceivingAccount;
            TransferAmmount = criteria.TransferAmount; 
            Execute();
        }
 
        #endregion
 
        #region Methods
 
        private void Execute()
        {
            if (TransferAmmount > OriginatingAccount.Balance)
            {
                Status = TransferStatus.Failed;
            }
            else
            {
                OriginatingAccount.Debit(TransferAmmount);
                ReceivingAccount.Credit(TransferAmmount);
                Status = TransferStatus.Success;
            }
        }
 
        #endregion
 
    }
}

 

TransferCriteria.cs

namespace AccountSample.Domain
{
 
    public class TransferCriteria