Showing posts with label methods. Show all posts
Showing posts with label methods. Show all posts

Wednesday, November 11, 2015

Stop giving me so much static

Let's talk about the keyword static for a moment.

There are two different uses for this in our code.  It can be applied to methods or it can be applied to variables.

When static is applied, it disassociates the item being declared from any specific instance of the class.  For methods, this means that the method can be called by anyone at any time without creating a new class of that type.  This can be particularly handy for utility functions, initializers, or any other chunk of code that wants to run without caring that it's about a particular person, bill, or list of stocks.  For variables, it mostly means that the value is shared across all instances of the class.

Static Methods

Static methods can be called without making a new class.  For instance, it could be used for a factory method, which is a design pattern that calls for objects to be built by calling a function to do so.

public class Person {
    ...
    public static Person createPerson(firstName, lastName) {
        ...
    }
}

You can call createPerson without having a Person object, like so:

Person.createPerson("John", "Doe");

Many utility functions are declared as static, and as a general rule they will need to operate free of any context supplied by objects.

Static Variables

Static variables come from the same basic idea of being disassociated from any specific instances of the class.  They're useful for maintaining overall context, for instance, if we want to keep track of how many Person objects we've created we could write something like this:

public class Person {
    ...
    private static int numberCreated = 0;
    ...
    public static Person createPerson(firstName, lastName) {
        ...
        numberCreated ++;
        ...
    }
}

Now, every time we invoke createPerson, we'll also be incrementing the value of numberCreated, and it will always be available as a statistic our programs can check.

Many times, inexperienced programmers will overuse static methods, because when they first start creating methods and functions, they just call them from main.  Your main method has to be static, and if you try calling non-static methods directly from it, the compiler complains.  So the path of least resistance is often seen as just making those other methods static too.  This does seem to work, but it goes against object oriented design principals, and it's best to nip this in the bud.  So they start with this:

public class OutputTest {
    ...
    private PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        print("This is a test");
    }
    ...
    public void print(String message) {
        outputStream.println(message);
    }
}

But that fails because you can't call 'print' from a static context, so they change it to this

public class OutputTest {
    ...
    private PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        print("This is a test");
    }
    ...
    public static void print(String message) {
        outputStream.println(message);
    }
}

That fails, too, because outputStream isn't static, so they change the code again:

public class OutputTest {
    ...
    private static PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        print("This is a test");
    }
    ...
    public static void print(String message) {
        outputStream.println(message);
    }
}

And they're satisfied.  Everything works now!  I call this the static cascade, and I recommend avoiding it.  If you did not originally intend for these items to be shared and free of binding to specific objects, then don't go changing them to act that way.  

Get used to writing object oriented code.  Do this instead:

public class OutputTest {
    ...
    private PrintStream outputStream = ...;
    ...
    public static void main(String [] args) {
        OutputTest test = new OutputTest();  //The key!
        test.print("This is a test");
    }
    ...
    public void print(String message) {
        outputStream.println(message);
    }
}

While it might be a bit more of a conceptual leap, it's actually a far smaller change to the original code, and it opens your class up to being more adaptable to use in other systems.  It's also a necessary step towards object oriented thinking, so you might as well get it over with now.

It's the final countdown!

Many times we declare a variable that we really ought not to change, at least in some particular spot.  We can just remember not to change it, and that might work.  A better solution is to let the language keep us from even trying, and Java offers us the final keyword for this purpose.

A variable declared as final cannot be changed, so maybe the word 'variable' does not even really apply, so we can call them 'constants' if we like.  This has a couple of different purposes.  First off, we might want to set up a constant like an approximation of pi for use in a class that does geometric calculations:

static final float pi = 3.14159;

Now any code that tries to change pi will be flagged by the compiler as a problem.

Another use for the final keyword is to make sure that methods don't modify parameters we've given them:

public void calculateValue (final int quantity) {

This would make sure that we don't accidentally change quantity somewhere inside the calculateValue method.

The compiler is able to simplify the code that it generates for variables declared as 'final', so we get that benefit, too.  In short, it's a good idea to declare things as final whenever it's reasonably possible to do so.

Tuesday, December 9, 2014

Everything is Awesome... Well, with one Exception

Let's talk about my old buddy Murphy.  I've been developing software for...  is it really a quarter of a century now?  And his law is ironclad.  Things WILL go wrong.  Things will always be going wrong, I suppose, unless and until a perfect computer is combined with a perfect programmer.  I don't think I'll worry about my day job on that account just yet.

When a program encounters an error condition, it can give up in a panic or it can attempt to handle the situation.  If you try to open a picture in your favorite photo editor, and it turns out that despite the name of the file, it was actually a word processing document, you probably wouldn't want the software to crash.  If you deploy a new program at work and it chokes because you forgot to feed it all the command line parameters it needs, you probably want it to tell the operations staff what went wrong, rather than giving them the precious gift of a crash report with no supporting information.

Back in the old days, we had several methods of dealing with these sorts of errors.  Sometimes there would be a particular value that would only come back from a function is something had gone wrong.  Maybe you expected a result of 0 or higher, so if it came back as -1 you knew you had a problem:

    returnCode = deployLandingGear();
    if (returnCode == -1) {
      printf("ERROR:  deployLandingGear failed.  Please check operations manual.");
      return;
    }
    // deployLandingGear worked.  We can go ahead and attempt to land on Mars
    land();

Now maybe your code is not as mission critical as that, but the point remains that we wound up sticking our error handling there into the main body of our code.  At best this was annoying.  At worst we failed to account for possible errors, but of course not because the project manager was pressuring us to get code shipped.

Enter the Exception.

Exceptions are pretty much what they sound like.  Conditions that are outside the normal expectations you have for what your program ought to be doing.  Your 'deployLandngGear' method ought to work pretty much every time, unless there's something physically wrong with your lander.  Exceptions give us a method of keeping our code nice and clean, while giving us an effective 'hook' to deal with problems as they arise.

Exceptions require just a bit of coordination between calling and called methods.  Any method that wants to be able to notify callers of problems should specify this fact in its method signature.

So the header for the method 'deployLandingGear might be written like this:

    public void deployLandingGear() throws HardwareException {
      

'HardwareException' is an arbitrary name I just made up, but it has to refer to a valid class that extends Exception.  I could have had it just say 'throws Exception', actually, but it's generally better to have it be more specific than that.  This is particularly the case if, for instance, you wanted to have the constructor for the HardwareException accept parameters such, for instance, how far the gear had gone before the problem occurred, which subsystem failed, etc.

So HardwareException might be declared like this:

    public class HardwareException extends Exception {

And perhaps it has a constructor like this:

        public HardwareException(Subsystem failureIn, int percentComplete, boolean canContinueWithFailure) {

Of course, when it comes right down to it, I don't work for either NASA or SpaceX and I have no idea what their protocols might be in the event of such a failure.  Let's assume though, that the landing gear subsystem is consider deployed 'enough' if it's at least 85% deployed, and that it reports its position as an integer of said percent deployed.

Let's see what happens in one case of deployLandingGear failing.

    public void deployLandingGear() throws HardwareException {
      ...
      if (false == landingGear.isCycleComplete()) {
        throw new HardwareException(landingGear, landingGear.getPosition(), landingGear.getPosition() > 85);
      }

Here we have introduced the keyword 'throw', which fulfills the contract specified by the 'throws' clause in the method signature.  We call this 'throwing an exception' and it pretty much is exactly that.  When we throw an exception it's kind of like returning from the method, except that there is no return type specified, required, or allowed.  Also, the control flow in the calling program will be different.

If we try to write code to deploy the landing gear and fail to deal with the HardwareException, The Java compiler will complain.  Your code will simply not compile.  In order to call it you will have to use a special construct called a try/catch block.  This syntax is often confusing when you first encounter it, but basically, we are going to try to do something, and if it fails and throws an exception, we're going to catch that exception and deal with it.

It looks like this:

    try {
      deployLandingGear();
      land();
    }
    catch(HardwareException landingError) {
      System.out.println("Hardware failure.  Landing may not be possible.  Error was: " + landingError );
    }

OK, now we see that the actual code block for 'deployLandingGear' and 'land' is a bit cleaner than it was using the older style.  Of course, this would be much more the case if we had more than two lines of code in there.  We also see (and this is actually not a great way to write the code) that in the event of a failure, control passes down into the catch block, eliminating all chance that you might ignore the error due to a lack of coffee and try to land anyway.

In reality it's probably better to allow the HardwareException to pass further up to a higher level master control function,  You can do this automatically by not having the try/catch structure at this level and declaring that this method also throws such a beast.  You can also do it manually by simply calling 'throw landingError' inside the catch block.

You can have multiple 'catch' blocks one after the other, if you need to account for several different exception types.  You can have a 'catch' block catch a superclass of your exception and still catch it, so 'catch (Exception e)' would have worked fine here.  It gets involved, and there's no one right answer for all situations.  You must let your system requirements and sometimes your personal preferences guide you.

Finally (ha ha) there is the 'finally' block.  This is optional and can be placed after all catch blocks.  This is code that WILL be run, whether you hit an exception or not.  Once either the try block is finished, or any catch block is finished, your finally block will be run.  This can be handy if you absolutely must release some resources or post a message or something.

    try {
     doSomethingThatCouldFail();
     System.out.println("Succeeded.");
    }
    catch(Exception e) {
      System.out.println("This is why you fail: " + e);
    }
    finally {
      System.out.println("Do or do not, there is no try.");
    }

You can't ignore exception handling, but it's possible to do it poorly.  The better you deal with things that go wrong, though, the more robust and resilient your software will be.  If you can change some setting and make another attempt, so much the better.

Wednesday, November 12, 2014

Functions

Functions, and their close cousins methods, are fundamental to software development.  Syntactically, there is no real difference between the two.  They really only differ in terms of the context in which they operate.  Technically, functions should not depend on anything except the information they are given.  If you call a function called 'add' with 2 and 3 as parameters, it should always return 5 regardless of what else might be going on inside your system.  A method called 'add' might only be given one parameter, and be expected to operate in a context where it is aware of some other number to which the parameter will be added.  Please note that it quite possible to write your code so that it does not abide by these rules, but it's probably not in your best interest to do so.

Suppose we want to write that function mentioned above, 'add'.  Furthermore, let's suppose we want it to be made available to any class that wants to call it, and that it is expected to do only integer (whole number) arithmetic.  We might write it like this:

    public static int add ( int number1, int number2 ) {
        return number1 + number2;
    }

This is of course simplistic, and probably better accomplished in-line where you need to perform the addition, but that's the nature of an example.

This function should be easy enough to understand once you understand the required syntax.  It starts off by being declared 'public', which means any other class can call it.  By declaring it 'static' we've indicated that we do not wish to have to create an object in order to call the function.  It has to give back an integer value to the code that calls it, so we put that up there, too.  Then we have the name of the method, followed by parentheses.  Inside the parentheses is a list of parameters (which can be empty).  Parameters are just variables given to a function from an outside source.  Note that every time it's given the same set of parameters it will give back the same result.  There are no hidden variables or shared secrets that will affect its operation and change the return value.

If I want to use add in this same class I can just write:

    public static void main(String[] args) {
        int answer = add ( 2 , 3 );
        System.out.println("The answer is " + answer );
    }

This will call 'add', which will see number1 as having the value 2, and number 2 as having the value 3.  Then add will then return 2 + 3, which is of course 5.  Because it's a return value, we can use the equal sign to set 'answer' to the returned value.  So 'answer' becomes 5, and we print out:

The answer is 5

Naturally, most useful functions will do a bit more than this, and they can in fact get very complicated, calling other functions and methods to do all sorts of tasks.  Returning a value is strictly optional, but you must declare the 'return type' for every function you write.  If you do not wish to have a return value, you can declare the return type as 'void'.  If you do declare a return type that is not void, you must ensure that you return an appropriate value when your function ends.  This would be an error and would fail to compile:

    int badAdd( int value1, int value2 ) {
        if ( value1 < 100 ) {
            return value1 + value2;
        }
     //Fails to compile!
    }

I've introduced another new concept here, a conditional statement in the form of 'if'.  This is another key concept, the idea of taking some action only if a specific condition is met.  In this case, if the value of 'value1' is less than 100 we'd execute all of the code between the brackets immediately following the if.  Technically the brackets are not required if you only have one line of code, but now forget I ever said that and just use them.  Many hours have been lost to misunderstandings that crop up from failing to use brackets where they can be.  Don't waste minutes (or worse, fail to notice a problem) by saving a half a second of typing and a couple of characters of disk space.

There are ways to get around returning a value in case of error conditions, which I will discuss under the topic of Exceptions at a later date.

View code here