Monday, December 15, 2014

Inheritance Part 2: That's Rather Abstract, Isn't it?

I hope you've been browsing through Java source code outside of your own and the small samples I've provided on this site.  Reading and trying to understand code can only help you in the long run.  You will encounter different code styles, you will see interesting data structures, API calls you didn't know existed, all sorts of stuff.

Today I'm going to talk about something you might have seen, but if you haven't, it's no big deal.  It is the keyword abstract.

We use abstract as part of the definition of a class, which indicates that while it is in fact a valid class file, nobody is allowed to create an object of that type.  That doesn't mean that it's useless though.  Remember, we have inheritance on our side.  You can create concrete classes (concrete is just the opposite of abstract, indicating a class from which you can create objects.  No special keyword is needed for that) that extend abstract classes all you want.

We also use abstract as part of method signatures when we want to create the idea of a particular method call but wish to defer the actual implementation of said method to our concrete classes.  This is similar in concept to interfaces, and sometimes it's more of a personal decision as to which you will use.

You see a lot of abstract classes in use when you look through frameworks, which are basically bodies of code designed to accomplish some function but with the details left up to the final developer.  There are business frameworks, logging frameworks, GUI frameworks, reporting frameworks, the list is very long.  Usually the trigger for creating a framework is when someone realizes they've just written the same thing for the fifth or tenth time, and that the differences between the various programs are relatively minor.  Maybe you query a database, format a report and write it to a file, and it's always the same except for the details of which fields you look for and print.

This is a great application for abstract classes.  Let's take a look at a simple one:

public abstract class Report {
}

Well, that's about as simple as it gets.  To be fair it's also about as useless as it gets but never fear, we'll fill in some details later.  Actually it's not entirely useless.  You might want to have a set of classes that all share a common base class so that you can collect them into a Collection of some sort, although you could also make this work with an interface.  Abstract classes really come into their own when you include some code within them.  So let's write something.

public abstract class Report {
    public abstract void GenerateOutputLine(String dataLine);
    public abstract String ReadData();

    public void runReport() {

        String dataLine = null;
        while( null != ( dataLine = ReadData() ) ) {
            GenerateOutputLine(dataLine);
        }
    }
    
}

OK, that was simple enough.  We have defined not only our abstract Report class, but made it do a little bit of work for us.  It calls ReadData until that method returns a null, and then takes the value returned from ReadData and passes it to GenerateOutputLine.  Of course, you can't actually run this report, you need to create at least one concrete class based on it.  That could be a simple test driver designed to make absolutely certain that your framework behaves properly with known data.  It could be something that reads from one file and writes to another, which would basically give you a 'copy file' command.  It could be something that reads one file, does some calculations or modifications, and then spits out the changed data.  The options are nearly endless, despite the fact that this is a highly simplified example.

Let's write a simple test driver:

public class ReportTester extends Report {

    public static void main(String[] args) {
        ReportTester rt = new ReportTester();
        rt.runReport();
    }

    String [] dataLines = {
        "one", "two", "three", "four", "five"
    };
    int lineNumber = 0;

    public void GenerateOutputLine(String dataLine) {
        System.out.println(dataLine);
    }

    public String ReadData() {
        String toReturn = null;
        if (lineNumber < dataLines.length) {
            toReturn = dataLines[ lineNumber++ ] ;
        }
        return toReturn;
    } 
}

Now if we were to run 'ReportTester' we'd get the output:

one
two
three
four
five

The main driver of the program is still in that 'Report' superclass, but the implementation details have been kept in ReportTester.  We run the code in Report, and it calls the methods it finds in ReportTester to do the work.  ReportTester itself is quite simple.  In addition to a simple main that does nothing more that create ReportTester and run it (actually running that method from the superclass Report), it defines the specific implementations for our two abstract methods.  One method does nothing more than write to the console and I hope I don't need to explain that.  The other goes through an array of Strings, returning the next one in the array and increasing the index value each time it's invoked until it reaches the end of the list, at which time it returns a null which triggers the end of the loop in 'runReport'.

This is of course tremendous overkill for a program of this size, but as your projects get more involved, the ratio of abstract code to concrete code is likely to change quite a bit.  System maintenance gets easier and faster, and your software gets more robust.  After all, a bug fix to a framework may take care of issues across several dozen different specific implementations of different reports.

I will of course revisit this topic later, because we've only scratched the surface of the possibilities brought on by object orientation.  With enough small components intelligently built, one can assemble new programs almost like putting together Tinker Toys or Lego.  One's systems can be defined in large part by configuration files, and changed easily at will, all without writing, testing or deploying new code.


Thursday, December 11, 2014

Good Practices: Coding to the Interface

You will sometimes hear someone recommend that you 'code to the interface'.  What the heck does that mean?

Well, I'm going to get deeper into just what interfaces provide for you, and why you should both create and use them in a later post, but a simple example can be shown with the very common Collections interface "List".

Using List is very common, although a lot of people will begin using ArrayList directly.  It's not that there's anything precisely wrong with doing this,  In fact, probably nine or more out of ten times the only thing it will cost you is a few extra keystrokes.  However, it serves as a good example in a section of code with which many of us get familiar early.

Suppose I wanted to have a list of active elements for a game.  I could write this code to have it available within my GameData class:

    private ArrayList<GameElement> gameElements = new ArrayList<GameElement>();

And then I could have a getter method:

    public Arraylist<GameElement> getGameElements() {
        return gameElements;
    }

That's simple enough, right?  So I go ahead and write a bunch of code within the rest of my game, happily accessing all the elements they need to function:

    public void addElementToGame(GameElement newElement) {
        ArrayList<GameElement> allElements = myGameData.getGameElements();
        if (false == allElements.contains(newElement) {
            allElements.add(newElement);
        }
    }
    ...
    public void walkAllElements(ElementChecker myChecker) {
        ArrayList<GameElement> allElements = myGameData.getGameElements(); 

        for(GameElement element : allElements ) {
            myChecker.checkElement(element);
        }
    }

etc. etc. etc.

It's not that there is anything wrong with this.  In fact, you're feeling pretty good, you even made a method that accepts a worker object as a parameter and invokes some code on that worker object for each item in the list.

Then the reports start coming in:  Sometimes the game gets very slow.  It's generally after someone has been playing for a long time.  Eventually, you pin it down to your element management code.  It's fine with a few elements but once you get to having hundreds, certain operations get bogged down.  At the same time, you realize that you'd love to prioritize these game elements by allowing inserts to happen at the start of the list for faster processing.  That's got you down all weekend as you try to figure out what to do.

The light at the end of the tunnel comes when you review the documentation for the Collections framework and discover LinkedList.  It might be a bit slower with smaller amounts of data, but it sounds like it will behave better for you with the large data sets you're processing and it offers the side benefit of being able to easily handle quick inserts at the start of the list.

That's when you suddenly get annoyed.  You make the change in GameData and from all the red markers that show up on your screen you realize that you've got seven hundred and fifty places in your code base where you have to do nothing more than change the word 'ArrayList' to the word 'LinkedList'.  This is all fixable, but it's going to take up a significant chunk of your time.  Most of the code does not even have to change in any other way, maybe you've got two or five places where you will need to explicitly refer to methods that are specific to LinkedList, bu the rest are just mechanical updates of the code.

This is where coding to the interface could have saved you some time and effort.  If, instead of referring to 'ArrayList' everywhere, you had just used 'List', all or at least most of your code would have otherwise remained exactly the same.

The declaration of gameElements could have been written this way:

    private List<GameElement> gameElements = new ArrayList<GameElement>();

Your walkAllElements could have looked like this instead:

    public void walkAllElements(ElementChecker myChecker) {
        List<GameElement> allElements = myGameData.getGameElements(); 

        for(GameElement element : allElements ) {
            myChecker.checkElement(element);
        }

Note that nothing has really changed here except for changing 'ArrayList' to 'List' in some places.  This is because 'List' is an interface, which is really a kind of contract.  It forces an object that implements it to support a set of defined methods, and both ArrayList and LinkedList do this.

    public class ArrayList implements List {

    public class LinkedList implements List {

Now this just scratches the surface of interface usage.  Later on we'll get into creating and using your own, and how that can save you even more time and energy, and bring your coding to a new level.  For now though, just be aware that classes can implement interfaces, and that whenever possible, it's better to write your code to care only about said interfaces, and not to give a hang about the specific object type it's dealing with.  Get used to it, let it become your natural way of operating.  It will serve you well later on.  Eventually you will be creating your own interfaces, which can serve as the control points for widely varying objects.  I can drive a car from any manufacturer, because they all offer me the same set of controls.  I do not need to be licensed to drive a Subaru, separately licensed to drive a Volvo, and have a learner's permit for a Ford.  Interfaces are like specifying 'gas pedal, brake pedal, steering wheel'.  Oh, and objects can support arbitrary numbers of interfaces, so in reality my car is defined something like this:

    public class S60 implements Driveable, GasPowered, Geartronic, PushbuttonStart, KeylessEntry {

There would no doubt be a lot more if I sat down to think about it all.  The point is, once I understand how to drive a car, I can drive pretty much any car, because they ALL implement Driveable.  If I go to my local Avis and rent something, I may not know about all the features, the navigation system may be a complete mystery to me, but man, I know how to work a steering wheel.