Feeds

Read, test, don't repeat - how to avoid code complexity

Emergent Design: Lessons from Y2K

Internet Security Threat Report 2014

Book extract, part four Redundancy, testability and readability are key to building simple and maintainable code. In the fourth extract from his book, Emergent Design: The Evolutionary Nature of Professional Software Development, published by Addison Wesley, Scott Bain tackles the problems and principles involved.

We all saw an example of redundancy rearing its nasty head just a few years ago.

The Y2K remediation effort cost countless billions of dollars, scared the heck out of entire countries, and in general disrupted progress in the technology sector. Many companies basically stopped doing any kind of new projects in the latter part of 1999, because they were putting their resources into fixing the Y2K bug.

What made Y2K hard to fix? Was it a complex problem? No! Not at all. Systems all over the world had stored the year portion of the date in innumerable records as a two-digit number, rather than a four-digit number. Not a complex problem at all.

Except that it appeared in billions of places, all of which had to be found and changed.

We can criticize the programmers who wrote the original code this way, but the fact is I find redundancies in my own code and the code of colleagues all the time. It is easy to do; it tends to creep in on you, especially when you are creating an extension to a new system. Also, some aspects of the languages we use can promote redundancy. Consider the following code:

public interface Weapon{
  public void load(int rounds);
  public int fire();
  public void setSafety(boolean on);  
}

public class Pistol implements Weapon{
  private int myBullets;
  private boolean safety = true;

  public void load(int bullets){
    if(bullets<=6){
       myBullets = bullets;
    } else {
      System.out.println("Pistol only holds 6 bullets");
    }
  }

  public int fire(){
    int rval = 0;
    if(safety){
       System.out.println("The Safety is on");
    } else {
       if(myBullets > 0) {
          System.out.println("Bang!");
          myBullets = myBullets - 1;
          rval = 10;
       } else System.out.println("Click!");
    }
    return rval;
  }

  public void setSafety(boolean aSetting){
    safety = aSetting;
  }
}

public class TommyGun implements Weapon{
  private int myBullets;
  private boolean safety = true;

  public void load(int bullets){
    if(bullets<=50){
       myBullets = bullets;
    } else {
      System.out.println("TommyGun only holds 50 bullets");
    }
  }

  public int fire(){
    int rval = 0;
    if(safety){
       System.out.println("The Safety is on");
    } else {
       if(myBullets > 9) {
          System.out.println("Budda Budda Budda!");
          myBullets = myBullets - 10;
          rval = 100;
       } else System.out.println("Click!");
    }
    return rval;
  }

  public void setSafety(boolean aSetting){
    safety = aSetting;
  }
}

There are lots of redundancies here - the way setSafety() is completely identical in both Pistol and TommyGun, for example. If I want to change the way this works - make the state persistent by writing it to the disk every time, for instance - then I must remember to change it in both places.

The Java and .Net interface type leads me to this inherently; it tends to create redundancies among the implementing classes. This is because the interface cannot have any actual implementation, so I cannot put common implementation into it, and allow the implementing classes to inherit.

What if I used an abstract class instead? Look at the following code:

public abstract class Weapon{
  protected int myBullets;
  protected boolean safety = true;  

  public abstract void load(int rounds);
  public abstract int fire();

  public void setSafety(boolean aSetting){
    safety = aSetting;
  }

}

public class Pistol extends Weapon{

  public void load(int bullets){
    if(bullets<=6){
       myBullets = bullets;
    } else {
      System.out.println("Pistol only holds 6 bullets");
    }
  }

  public int fire(){
    int rval = 0;
    if(safety){
       System.out.println("The Safety is on");
    } else {
       if(myBullets > 0) {
          System.out.println("Bang!");
          myBullets = myBullets - 1;
          rval = 10;
       } else System.out.println("Click!");
    }
    return rval;
  }
}

public class TommyGun extends Weapon{
  
  public void load(int bullets){
    if(bullets<=50){
       myBullets = bullets;
    } else {
      System.out.println("TommyGun only holds 50 bullets");
    }
  }

  public int fire(){
    int rval = 0;
    if(safety){
       System.out.println("The Safety is on");
    } else {
       if(myBullets > 9) {
          System.out.println("Budda Budda Budda!");
          myBullets = myBullets - 10;
          rval = 100;
       } else System.out.println("Click!");
    }
    return rval;
  }
}

I have eliminated some of the redundancy already: The setSaftey() method is now in one place, inherited by both Pistol and TommyGun. Also, the data members myBullets and safety were common, so I put them in the superclass too and made them protected so the subclasses could still access them directly.

There's more I could do, of course. The weapons both operate in a conceptually similar way, only the details vary. If I am lucky enough to know the Template Method pattern, I could pretty easily get rid of all the other bits of redundancy here, without sacrificing any of the uniqueness of these two weapons.

Redundancy and coupling

But haven't I introduced coupling in order to deal with the redundancy problem? The abstract superclass puts one rule in one place, but it also means that a change in the superclass (Weapon) will have an effect on the subclasses (Pistol and TommyGun). This is inheritance coupling, certainly, so have I traded one problem for another?

Top 5 reasons to deploy VMware with Tegile

More from The Register

next story
Netscape Navigator - the browser that started it all - turns 20
It was 20 years ago today, Marc Andreeesen taught the band to play
Sway: Microsoft's new Office app doesn't have an Undo function
Content aggregation, meet the workplace ... oh
Sign off my IT project or I’ll PHONE your MUM
Honestly, it’s a piece of piss
Return of the Jedi – Apache reclaims web server crown
.london, .hamburg and .公司 - that's .com in Chinese - storm the web server charts
NetWare sales revive in China thanks to that man Snowden
If it ain't Microsoft, it's in fashion behind the Great Firewall
Chrome 38's new HTML tag support makes fatties FIT and SKINNIER
First browser to protect networks' bandwith using official spec
prev story

Whitepapers

Forging a new future with identity relationship management
Learn about ForgeRock's next generation IRM platform and how it is designed to empower CEOS's and enterprises to engage with consumers.
Why cloud backup?
Combining the latest advancements in disk-based backup with secure, integrated, cloud technologies offer organizations fast and assured recovery of their critical enterprise data.
Win a year’s supply of chocolate
There is no techie angle to this competition so we're not going to pretend there is, but everyone loves chocolate so who cares.
High Performance for All
While HPC is not new, it has traditionally been seen as a specialist area – is it now geared up to meet more mainstream requirements?
Intelligent flash storage arrays
Tegile Intelligent Storage Arrays with IntelliFlash helps IT boost storage utilization and effciency while delivering unmatched storage savings and performance.