What's Wrong With This Code (#19)

Leroy was shocked when the source code appeared. It was familiar yet strange, like an old lover's kiss. The code was five years old – an artifact of Leroy's first project. Leroy slowly scrolled through the code and pondered his next move. It wasn't a bug that was bothering Leroy – there were no race conditions or tricky numerical conversions. No performance problems or uncaught error conditions. It was all about design …

public class BankAccount
{
    
public void Deposit(decimal amount)
    {
        _balance += amount;
        LogTransaction(
"Deposited {0} on {1}", amount, DateTime.Now);
    }

    
public void Withdraw(decimal amount)
    {
        _balance -= amount;
        LogTransaction(
"Withdrew {0} on {1}", amount, DateTime.Now);
    }

    
public void AccumulateInterest(decimal baseRate)
    {
        
decimal interest;

        
if (_balance < 10000)
        {
            interest = _balance * baseRate;
        }
        
else
        {
            interest = _balance * (baseRate + 0.01);
        }
        LogTransaction(
"Accumulated {0} interest on {1}", interest, DateTime.Now);
    }

    
void LogTransaction(string message, params object[] parameters)
    {
        
using(FileStream fs = File.Open("auditlog.txt", FileMode.OpenOrCreate))
        
using(StreamWriter writer = new StreamWriter(fs))
        {
            writer.WriteLine(message, parameters);
        }
    }

    
public decimal Balance
    {
        
get { return _balance; }
        
set { _balance = value; }
    }

    
decimal _balance;
}

"Times have changed, and so I have, fortunately", Leroy thought to himself. "And so will this code…"

To be continued…

posted on Sunday, March 30, 2008 11:55 PM by scott

Comments

Sunday, March 30, 2008 9:48 PM by Jonathan

# re: What's Wrong With This Code (#19)

Create a strategy for accumulating interest, rather than the AccumulateInterest() method that could grow over time into multiple if's, whereas's and whatnot's?
Sunday, March 30, 2008 10:00 PM by Ryan Gray

# re: What's Wrong With This Code (#19)

Wild guess - logging the transaction shouldn't be part of this class, as it violates the SRP. I'm thinking Decorator pattern to the rescue, but maybe all the IoC stuff I'm reading lately is scrambling my brain...
Sunday, March 30, 2008 10:18 PM by Jason Stangroome

# re: What's Wrong With This Code (#19)

Two things jump to mind:

Being an important finance related class, I'd like to see both pre- and post-logging.

Also, the 10000 in the if statement in AccumulateInterest and the 0.01 addition to baseRate in the else block are magical. This needs to be refactored out into a suitable arrangement depending on the behaviour they are there to implement.

Regards,
Sunday, March 30, 2008 10:41 PM by lynn

# re: What's Wrong With This Code (#19)

1) There needs to be a check for parameter values on all of the methods, contract for positive decimals only.

2) Balance property should be readlonly with init in the constructor.

3)There should be a single event labeled 'BalanceChanged' which can allow for logging or anything else.
Sunday, March 30, 2008 11:05 PM by Wilhelm Svenselius

# re: What's Wrong With This Code (#19)

I agree with the above. In addition, the AccumulateInterest method simply throws away its calculated value. Shouldn't it affect the account balance? Also, it ignores the case where the balance is negative, doesn't check that "baserate" is indeed a value in the expected range (it could be >1 or negative for all we know).

Furthermore, Balance exposes a public setter. Seems like a gross encapsulation violation since anyone could just set the balance to anything without logging a transaction.

Finally, the code isn't thread-safe. I wouldn't assume that reading or writing a decimal is necessarily, if ever, an atomic operation.
Sunday, March 30, 2008 11:50 PM by Ilia Jerebtsov

# re: What's Wrong With This Code (#19)

Unless I'm missing something, the AccumulateInterest function doesn't actually do anything aside from logging the (local) interest value, which means that in order to retrieve it, one would have to parse through the log.
Monday, March 31, 2008 12:01 AM by Laurent

# re: What's Wrong With This Code (#19)

May I add the suggestion to use the Balance property instead of the private _balance field ?
Monday, March 31, 2008 12:31 AM by Kalpesh

# re: What's Wrong With This Code (#19)

2 things

1) You could change the Balance by setting a property (no need to deposit/withdraw)
2) Validations on baseRate argument. User could pass in a negative value or -0.01 which will keep reducing the balance

Interest is getting calculated but not added to the balance :)

The replies above also make a lot of sense.
Monday, March 31, 2008 1:41 AM by Rik Hemsley

# re: What's Wrong With This Code (#19)

Where is the interest value actually added to the balance?
Monday, March 31, 2008 2:25 AM by Kamran Shahid

# re: What's Wrong With This Code (#19)

Decimal overflow might be expected as there isn't any check for the boundary condition.
Monday, March 31, 2008 3:26 AM by Zeb

# re: What's Wrong With This Code (#19)

interest does nothing ... it's a local variable, but it's being logged as being accumulated.

Also, I assume the transactional locking is done external of this class. Otherwise the balance will be wrong on concurrent updates.
Monday, March 31, 2008 3:31 AM by edddy

# re: What's Wrong With This Code (#19)

Other than than 10000 and 0.01 not being configurables and the log function in the same class

1. Not having error management in case the logging fails
2. Allowing direct access to setting "Balance"
3. Nothing checks that the amount to Withdraw is less or equals that balance
Monday, March 31, 2008 4:14 AM by cSpare

# re: What's Wrong With This Code (#19)

Afaik += and -= are not thread safe.
Monday, March 31, 2008 5:25 AM by Amr Elsehemy

# re: What's Wrong With This Code (#19)

Concurrency problems when updating at the same time?
Log might not be valid.
Monday, March 31, 2008 5:32 AM by LaptopHeaven

# re: What's Wrong With This Code (#19)

The algorithm is not mutual exclusive.
Monday, March 31, 2008 7:14 AM by scott

# re: What's Wrong With This Code (#19)

@Johnathon, @Ryan: yes - that's along the lines of what Leroy is thinking!


@Jason, @Lynn - both very valid points. I like the event idea. Facrtoring out the magic number is good, too.

@Wilhelm, @Zeb, @Rik, @Kalpesh, @Ilia - throwing away the calulated interest. Yes - that would be like an actual bug :) Perhaps Leroy should be writing some tests?

For everyone who mentioned concurrency problems, let's not worry about threading. We can documument the class as not being thread safe.
Monday, March 31, 2008 7:50 AM by Tim B

# re: What's Wrong With This Code (#19)

Okay, if we're focusing only on design here, I'd say the two most important refactorings would be moving interest rate accumulation and logging into separate classes. A bank account shouldn't be aware of the business rules around a rate table - it should ask for and be provided with the interest amount for its current balance. Likewise for accounts knowing how to log themselves.
Monday, March 31, 2008 8:29 AM by simonjohnroberts

# re: What's Wrong With This Code (#19)

I dont like the font, it's a bit small.

Plus I would never indent like that...

;)
Monday, March 31, 2008 3:16 PM by Zeb ...

# Strategy pattern?

Sounds like YAGNI to me. Don't just stick an abstraction in there to replace one If-Then statement. When the next 2-3 business rules come in and make the code yucky, then refactor out to a strategy.

http://www.ridgenet.net/~do_while/toaster.htm
Monday, March 31, 2008 11:33 PM by Christopher Steen

# Link Listing - March 31, 2008

Link Listing - March 31, 2008
Monday, March 31, 2008 11:33 PM by Christopher Steen

# Link Listing - March 31, 2008

ASP.NET How to print the current row number in an ASP.NET Repeater control [Via: Fritz Onion ] Sharepoint...
Tuesday, April 01, 2008 12:43 AM by Mikeon

# re: What's Wrong With This Code (#19)

Zeb ... has a point. You just can't put a pattern on everything. It's just not right.
Tuesday, April 01, 2008 9:18 PM by K. Scott Allen

# Testing Old Code Is Hard

WWWTC #19 presented a BankAccount class from a developer named Leroy and garnered some great feedback....
Tuesday, April 01, 2008 9:20 PM by scott

# re: What's Wrong With This Code (#19)

@Zeb, @Mike: agreed with YAGNI. Let's just see where this goes, however (and we always have to simply the real world to make it fit on a blog, unfortunately).
Tuesday, April 01, 2008 9:47 PM by BusinessRx Reading List

# Testing Old Code Is Hard

WWWTC #19 presented a BankAccount class from a developer named Leroy and garnered some great feedback.
Tuesday, April 01, 2008 9:58 PM by Simon.Fox

# re: What's Wrong With This Code (#19)

No one has mentioned using the Measurement patterns from Analysis patterns (Martin Fowler). These could help with a number of things relevant here.
Wednesday, April 02, 2008 8:21 AM by Community Blogs

# Testing Old Code Is Hard

WWWTC #19 presented a BankAccount class from a developer named Leroy and garnered some great feedback
Tuesday, April 08, 2008 10:08 PM by K. Scott Allen

# Following Principles

A dictionary definition of principle often uses the word &quot;law&quot;, but principles in software development...
Tuesday, April 08, 2008 10:32 PM by BusinessRx Reading List

# Following Principles

A dictionary definition of principle often uses the word &quot;law&quot;, but principles in software development
Wednesday, April 09, 2008 10:05 AM by Eric

# re: What's Wrong With This Code (#19)

Leroy is probably lamenting not writing this as a TSQL stored procedure.
Tuesday, April 15, 2008 10:41 AM by .Net World

# Following Principles

A dictionary definition of principle often uses the word &amp;quot;law&amp;quot;, but principles in software
Tuesday, April 15, 2008 10:41 AM by .Net World

# Testing Old Code Is Hard

WWWTC #19 presented a BankAccount class from a developer named Leroy and garnered some great feedback
Thursday, June 05, 2008 6:46 AM by Community Blogs

# Following Principles

A dictionary definition of principle often uses the word &amp;quot;law&amp;quot;, but principles in software