Showing posts with label directories. Show all posts
Showing posts with label directories. Show all posts

Tuesday, December 9, 2014

Importing and Packages - Customs won't like this.

Alright, I've been ignoring it, but the time has come to take on a fairly important issue when it comes to organizing your code.  The topic of the day is packaging.

Packages are really not much more than a set of directories (or folders if you prefer) into which your .class files are placed.  This becomes important once your projects get beyond two or three .java files, because you really want to avoid namespace clashes.  You don't even know what they are yet but I'll bet you're agreeing that they sound like something to avoid.  More importantly, proper packaging will help you to keep your workspace and brain organized.  Programming is hard enough, you don't need to make it worse on yourself.  I may keep a messy desk but I do try to have my source code laid out in a logical fashion.

Most of the time, we bundle up our .class files into something called a Jar.  This is just a file with a .jar extension, and it's actually in technical terms a .zip archive file.  This is convenient because a sizeable system can contain dozens or hundreds of .class files, and managing them individually will make you want to gnaw your foot off.

Now, we could go ahead and just put all of the .class files into one giant list.  But that's not a list you want to be looking at or searching through.  It is almost always possible to separate out the various parts of a project into logical subsections, and you should do this.

At the most basic level, you can control packaging by putting a line at the top of your source file like this one:

    package com.oopuniversity.packages;

It dictates that the compiled .class file should be placed in a subdirectory called 'com.oopuniversity.packages'.  When you want to refer to a class called 'PackageDemo' in your java code, you can do it either the hard way:

    com.oopuniversity.packages.PackageDemo = new com.oopuniversity.packages.PackageDemo();

Or you can do it the easy way.  Add an 'import' line up above your class declaration like this:

   import com.oopuniversity.packages.PackageDemo;

This applies to any class that is not in the same package as the class on which you're working.  For classes within the same package the import statement is unnecessary.'

IDEs make this whole process really easy.  You tell them to create a new package, and they do so, creating all of the necessary directories for both java and class files.  You try to create an object and they can automatically figure out that you need an import statement and add it for you.  When they build the code, it's placed in the correct location.

At first this may seem like a needless complication, but it pays off as soon as you're doing any work that is not extremely basic in nature.

The rules for package names are simple.  While it's not hard and fast, stick to lower case letters.  Separate individual words with periods.  You can call them anything you like, but it's generally a good idea to go from less specific to more specific.  Most of the standard packages that come in the Java libraries start with 'java.' and most third party packages start off with the domain name of the developer (this 'com.oopuniversity') and are then followed by as many identifiers as needed for your purposes.  It is a good idea to organize this way, because if you use the same package names as someone else does you could badly confuse your ClassLoader and this leads to really irritating situations when you try to get programs to actually run.  In short, 'util' is a pretty bad package name, but 'org.mycompany.util' is better.  'org.mycompany.secretproject.util' is even better yet.

In summary:

Organize your code into packages.
Specify the package name at the top of your source file.
Import the classes you need by specifying their packages in import statements.

There are specific packaging rules I like to follow when building systems, related to how the code itself is structured.  I'll talk about those rules when it's appropriate.  Those are going to be more on the order of recommendations, but it won't hurt you (much) to go along with them.

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

Project Organization Part 1: Directory Layout

In our first post we set up a simplified project called HelloWorld.  That is of course, a very standard first program in virtually any language, designed to provide a minimal functional program to provide a bit of user output.  That's all well and good, but in the interest of getting something built I glossed over or just plain ignored some aspects of project setup.

First and foremost, we allowed the source code and the compiled .class files to sit in the same location.  I was tempted to avoid that even for the first post, but eventually decided it was best to just get something done and tackle that in a separate post.  So here we are.

As a general rule, I greatly prefer to set up my projects with two main directory trees, one for source files and one for build products.  Typically I'll use the names src and build for these directories.  You will find that once you start using an IDE this structure will tend to be built automatically for you, which will make it transparent and natural.

So let's take HelloWorld and convert it to a better structure:



Now we've got a couple of buckets for our files.  For now we'll ignore packaging and just rearrange the files.  Since class files can be regenerated at any time, we'll just get rid of HelloWorld.class.  We will then move HelloWorld.java to its new home.


Now let's rebuild our class file to make sure we can still operate.  Go into the build directory and run javac, but this time you'll have to enter a relative path in order for the system to find it.  After that you can, if you so choose, do a test run to make sure it still works correctly.




I mentioned earlier that we're not going to discuss packaging, but I do want to quickly mention that we still have not gone far enough with moving our files around.  The reason for this involves the way that Java loads classes, and has no real implications until you start assembling slightly larger programs.  Suffice it to say that we're not done yet, but we can wait a while before we get into that.  Next time around, we'll talk about functions, which will be a key concept you must understand before we get around to creating objects.