On setters, constructors and modelling reality

In my experience there are almost no situations where a setter method is a good idea. The wholesale replacement of a piece of an object’s internal state at arbitrary times leads to a class of interaction defects that are horribly difficult to track down. Most of the time solving them involves hooking up the debugger and stepping through going, ‘its fine here. Its fine here. Its still fine here. Its not fine here. Who called setName with an invalid value?’ and then debugging the call history of the method that made the bad call. Or alternatively, knowing that you have to call something like ‘accountService.updateBalance(account)’ before you can say ‘account.getBalance()’ or you’ll get a null back.

A class with all fields marked as final and initialized through its constructor is robust and easier to use than one with setter methods. You know that any time you have a reference to an instance of it, it’s ready to use and you can call methods without worrying about random fields not being set.

Note that just because all the fields are final and there are no setters, this doesn’t mean that the object is immutable. It just means that its behaviour more closely models reality, and your interactions with it will be far more meaningful. account.credit(amount) and account.debit(amount) instead of account.setBalance(amount). The balance is the sum of all the credits and debits, not something anyone can arbitrarily change (although I’d love it if I could just call my bank and say ‘please set my account balance to 1 million pounds’).

Which brings me on to my next point. I often see setters used where information relating to an object is not known at construction time, and needs to be added later. This is usually a symptom of choosing the wrong class to store that data. To continue the bank account example, a bank account (at least here in the UK) has certain properties. It has a sortcode and an account number, and probably an account name. These properties are pretty much fixed for the life of the account. (Sometimes they do change, but that is often dealt with as an account closure and opening of a new account with the funds transferred). There are certain things that an account does not have, which may surprise you.

A bank account does not have a balance. Not in the same way it has an account number. What an account does have is a series of credits and debits over time. When I call up my bank and ask for my account balance, what I’m really asking for is ‘the balance as of right now’. The snapshot of money in and money out since the account was opened that adds up to what is in it today.

Now lets say we want to encapsulate the concept of a balance instead of having to calculate the value from the list of credits and debits. I need the account it relates to, the point in time I’m interested in and, of course, the balance of the account at that time. A Balance class might therefore be constructed as ‘new Balance(Account account, Date asOf, Money amount)’. Those 3 pieces of information combined are immutable. They will never change. No matter how often I check my balance from the 10th of April it will always have the same value.

Thus its fine for a Balance to have an Account (and in fact completely meaningless for a Balance not to have an Account), but an Account does not have a Balance. Getting this kind of relationship right is at the heart of OO design.

Advertisements

A bad citizen in Javaland

You are born with no body parts. One by one, your organs are pushed into you. Should anyone attempt to interact with you before your organs have finished arriving you will die. Luckily this doesn’t happen. Once all your organs are settled you are ready to start a job (life moves fast in Javaland). You ask the universe for the abstract concept of vehicular transportation. The universe gives you a car. You look at the car and tell yourself it’s a motorcycle. You try to climb on the roof and are thrown off. Catching your error you land back on your feet. Finally you try telling yourself the car is a car and that seems to work. You open the bonnet and take out the engine. You reach into the engine and take out the starter motor. You turn the starter motor until the engine starts. You take the steering wheel and pedals out of the car and use them to drive yourself to work.

During the day you need to borrow some money from a colleague. You pick up your colleague, reach into their trousers and pull out their wallet. You open the wallet and pull out some cash. You keep their wallet in case you need more money later. They also keep their wallet, remaining unaware that you have taken out some money and that their wallet is now in two places at the same time. Later, you both attempt to spend the same money twice. The universe explodes and is restarted. An identical copy of you lives your life again. Subtle changes to the fabric of reality prevent the universe from exploding this time. Your day continues.

You leave work with your friends. One by one they forget who you are and leave. You are alone. A man wearing a refuse disposal engineer’s uniform appears from nowhere and assassinates you, placing your body into a recycling bin. Your organs are donated to the next generation. You die. The end.

Enough bad software

I’m sick of it. Enough with the shoddy software. Today I shall be reporting my experiences evaluating Panda Software’s Titanium anti-virus:

Installation crapped out, ‘Pure virtual function call’ error. Made my machine reboot twice, then immediately proclaimed that the evaluation had expired, about 30 seconds after I’d installed it for the first time. Unacceptable.

While I’m on the subject, Norton AntiVirus 2006 should also be avoided like the plague. A friend of mine purchased it, and it proceeded to completely hose his system. The machine would take over 2 minutes longer to boot than before. Excel wouldn’t even start. It took me (someone considered to know a little about computers) nearly an hour to uninstall it. It finally took two safe mode reboots, shutting down (and disabling) half the windows services and two attempts at running the uninstaller to remove that little piece of scumware.