Wednesday, June 3, 2009

Replacing Booleans with Enums for Great Justice

From time to time I see method signatures that look like the following contrived example:


public interface Copier {
public void copy (boolean readOnly, boolean fuzz);
}


If someone calling this function doesn't have access to the documentation or source code, they won't be able to see the names of the variables, and therefore will not know that the first variable signifies read-only status, and the second whether or not to use fuzz. This leads to coding by guesswork, which is a bad thing.

Enumerations can fix this:


public interface Copier {
public static final enum ReadMode {READ_ONLY, READ_WRITE}
public static final enum FuzzType {NO_FUZZ, REGULAR_FUZZ}

public void copy (ReadMode readOnly, FuzzType fuzz);
}


This means that the developer does not have to look up documentation to see what each argument is for, and the compiler will shout at people if they mix up the parameters.

This way of doing things is also much more extensible. Suppose the code needed to handle a new type of fuzz for the latest version of the product: super fuzz. The FuzzType implementation could easily be replaced with the following:


FuzzType {NONE, DEFAULT, SUPER}


Although this breaks the API, it does so in a less destructive way than changing the type of the fuzz parameter - the code will still compile, anyone using a default clause in their case statements (or else clause in their if statements) will already be able to handle the API change, most of the time things will continue working and babies will not die. Another sticky situation occurs if a client iterates over the enumeration. If the client does this, then (according to Murphy's Law) the client will do something bad and things will break.

Replacing booleans with enumerations is not a new idea (others have suggested similar things) and, like anything, is not a silver bullet: an enum takes up space just like any other class, which is less efficient space-wise than using a boolean; a third possible value (null) is added which should be checked for; and the code becomes a little more complex. The main benefit from using an enum instead of a boolean is self-documenting code.

Tuesday, May 12, 2009

Doin' the backup

I've finally convinced myself that a) I should backup, and b) it's not all that expensive. Following jwz's advice, I bought myself a drive, stuck it in a USB enclosure and ran the backup command. After that was all finished I realised I had to disconnect the drive. This is where things got a little tricky. If I was running OSX or Windows, I could use the standard eject command (remove hardware on Windows); the disc would be unmounted, stopped, and (possibly) turned off. I wasn't too worried about turning the disc off because that happens when the plug is pulled. I also wasn't too worried about unmounting the disc because I know how to do that. What I was worried about was spinning the disc down.

When power is cut to a typical hard disc, one of two things happens. First, the head will just sit there between the platters, and if you shake the drive or drop it (or whatever), then the disc will get scratched and you will lose data. Second, (preferably) the head will perform an emergency retraction and get itself out from between the platters. This is a good thing because it means that it won't stay in there and delete your precious bits, but it's still not ideal because there is the potential for it to scratch your disc on the way home. So, ideally, the disc should be stopped before the power is removed.

But, in this case, I'm not running OSX or Windows - I'm running Linux, and Linux doesn't have a simple, built-in, all-in-one solution for this.

The first program I tried was scsiadd. Random Bits has a pretty good post on scsiadd, and I was hopeful, but I found that, for whatever reason, it didn't work for me.

sdparm is what I was looking for, and it works like a charm.

As a side note, I probably should also suspend the USB port as Yan Li suggests, but my kernel doesn't like that so I didn't bother.

The script that I've settled on (for now) is:

#!/bin/sh

mount /dev/sdb /media/backup

rsync -vaxE --delete --ignore-errors / /media/backup/

umount /dev/sdb

## spin the disc down
sdparm -C stop /dev/sdb

## wait for disc to spin down
sleep 10

echo "Done"

I'll probably add a few more lines to check stuff like whether something is actually at /dev/sdb but this script already does pretty much everything I need it to.