Tuesday, 26 April 2011

Good Programming Practice - avoiding the if statement

Those of us who have programmed for a while are fairly familiar with and comfortable using the if statement in code but the if statement if mis-used can be both confusing and a source of latent defects, that is defects which are not realised for a while and then jump out and bite you. What is the distinction between a good and a bad if?
A good if statement should always be valid whatever happens to the system so a method which creates a type of class to manage a port might look like:

if ( typeToCreate == "TCP" ) port = new TCPPort();
else if ( typeToCreate == "HTTP" ) port = new HTTPPort();

If you think about it, although we might decide to use a different port in the future, the logic always holds true that if the type is TCP, we will create a TCP port. If we need to expand the functionality of the port by extending its interface, we can do so without 1) touching the if statement and 2) invalidating the logic of the if statement. Ideally, we would then have an else statement which might throw an exception to ensure we are using a valid port type.
Now consider the logic below in a classic if statement:

if ( message.SubString(0,1) == "S" ) DoSomethingForStringMessage();
// Carry on with processing

Can you see what the problem is here? There is an assumption buried deep beneath the code that says a message which is a string ALWAYS has an S as the first character and a message which does NOT have an S is NEVER a string. This might seem perfectly reasonable and correct but what happens when somebody decides in 3 years time that X represents an extended string. These new messages will fail the logic test but possibly in a way that is not obvious. This is a very easy to create defect which might well pop its head up at the customer site!
I am not a Puritan but I believe in making code as reliable as possible. We would not accept an aircraft designer thinking something is a quick hack but yet we allow it in software because perhaps we don't give it the respect and effort that should be given to it. Of course, it would be unfair to bring this issue up without telling you about alternatives. Ultimately there are three things that can really help you 1) The compiler is your friend - use constructs that the compiler can check 2) OO design allows you to embed logic into classes and 3) Do not repeat yourself. If you need to ask the same question in multiple places, you are asking for one or more of them to fall out of sync.
Here is an example of using the compiler and OO to help you code logic strongly:

// Carry on

Here we introduce a class to represent the message (it might only be one class to begin with) and we create a method that is fairly generic and allows us to preprocess the message. In our case, we probably want a class to represent a normal message and one that represents string messages (which could inherit from the normal one). In this case, the string message PreProcess allows us to do whatever it is we needed to do inside this message with NO logical branching. Adding new messages with inheritance allows us to force people to consider the various methods that are available by making them abstract/pure virtual.
This leads to the 3rd principle which says in one place, and one place only, I will use some sort of data loader to decide which class to load for any new messages I create, this follows the safe method that the logic is always true since by definition, if I add a new message, I either have to extend or modify the logic in a single place to load my new handler class and this will remove the chance of a bug:

if ( message.SubString(0,1) == "S" ) CreateStringMessage();
else if (message.SubString(0,1) == "N" ) CreateNumericMessage();
else throw new NotImplementedException("No handler for message type");

If I add a new message which might also begin with S, I might need to do this:

if ( message.SubString(0,1) == "ST" ) CreateStringMessage();
else if (message.SubString(0,1) == "N" ) CreateNumericMessage();
else if ( message.SubString(0,1) == "S" ) CreateNewMessage();
else throw new NotImplementedException("No handler for message type");

The important thing being that the logic is in a single place and not buried somewhere down in a method. Using the exception mechanism for defensive programming is then an easy and quick way to fall over during testing if something has been forgotten.

Installing Driver for EdiMax 7711ln

Click here for a great article about how to get your 7711ln Wireless LAN network (PCI) card working in Linux. I am using Kubuntu 10.10 (kernel 2.6.35-28) and installed the card as normal not really expecting it to not work! Fortunately, EdiMax provide the driver on their website which is in fact a Realtek driver for the 3562 chipset (the driver actually builds for various flavours including this).
Ignore what it says in the readme file which is very complicated and confusing when confronted with all the options and simply follow the instructions on the link. You can ignore any errors about the lack of the /tftpboot directory.
In the line which says
sudo ifconfig ra0 inet up
, replace ra0 with the interface name for your wireless card which might be different. Type ifconfig by itself on the command line and you will see a list, probably including eth0, lo and either ra0 or wlan0/1. ra0 or wlan0/1 is your wireless card.
If you have a newer Linux, once you follow the instructions and reboot, you might find that the card is still not seen by the Network Manager in the system tray (you won't see any wireless networks) and that is probably because by default Linux will load the rt2800pci (old built-in) driver and this will take priority over the one you just built. To find out, type lspci -v at a command line and see what is written next to the entry for your wireless network card. It will start with something like:

05:01.0 Network controller: RaLink Device 3060
Kernel driver in use: rt2860
Kernel modules: rt3562sta, rt2800pci

If you have the problem, the driver in use will show rt2800pci, in my case it has loaded the driver rt2860 from the driver I built myself contained inside rt3562sta.
In order to force it to use your newly built driver, you need to blacklist the built-in driver. Simply open /etc/modprobe.d/blacklist.conf in a text editor (as sudo) and then add the following to the end (with a comment to help remember why you did it!):
blacklist rt2800pci

Reboot and the job's a good un'

Monday, 11 April 2011

Project DLL not copied into bin directory of web site

Simple situation. I reference a dll in the web.config of a site. I clicked "Add reference" and browsed to the directory where the dll was and added it with no problems. When it came to checking into source control and building, I got "Could not find type ..." which was muchos confusing.
It turns out that because the library I was referencing was the same version as one that I had installed in the GAC, it told the project to look in the GAC for the library rather than the location I had referenced. All very clever and very confusing.
In my case I could remove the library from the GAC and reference it again. This time I got a refresh file (which tells the project where to start looking for the library) which I checked in for safe keeping and all was good in the world. Nice!