Sunday, March 18, 2007

Encapsulation

This tutorial discusses the first fundamental object oriented principle which is encapsulation of information and functionality within the object.

Encapsulation

Encapsulation is the ability of an object to place a boundary around its properties (ie. data) and methods (ie. operations). Programs written in older languages suffered from side effects where variables sometimes had their contents changed or reused in unexpected ways. Some older languages even allowed branching into procedures from external points. This led to 'spaghetti' code that was difficult to unravel, understand and maintain. Encapsulation is one of three basic principles within object oriented programming languages.

Object variables can be hidden completely from external access. These private variables can only be seen or modified by use of object accessor and mutator methods. Access to other object variables can be allowed but with tight control on how it is done. Methods can also be completely hidden from external use. Those that are made visible externally can only be called by using the object's front door (ie. there is no 'goto' branching concept).

Class Specification

A class specifies the properties (data) and methods (actions) that objects can work with. It is a template or prototype for each of many objects made to the class design. The syntax for a class is:

["public"] ["abstract"|"final"]"class" class_name
["extends" object_name] ["implements" interface_name]
"{"
// properties declarations
// behavior declarations
"}"

The first optional group indicates the visibility or scope of accessibility from other objects. public means visible everywhere. The default (ie. omitted) is package (aka friendly) or visible within the current package only.

The second optional group indicates the capability of a class to be inherited or extended by other classes. abstract classes must be extended and final classes can never be extended by inheritance. The default (ie. omitted) indicates that the class may or may not be extended at the programmers discretion.

The third option of extends is described in the tutorial on inheritance.

The fourth option of implements is described in the tutorial on interfaces.

Let's use a simple box as an example of a class specification. The box has length, width and height properties as well as a method for displaying the volume. An example of how this might be specified in Java is:

public class Box
{
// what are the properties or fields
private int length;
private int width;
private int height;

// what are the actions or methods
public void setLength(int p)
{length = p;}

public void setWidth(int p)
{width = p;}

public void setHeight(int p)
{height = p;}

public int displayVolume()
{System.out.println(length*width*height);}
}

Note1: There is no main method in a class defining template!
Note2: Class names begin with a capital. Convention uses lowercase for all other names.

Properties (aka Field Variables)

Classes without properties or behaviour are not very useful. Properties in Java are sometimes called field variables. To declare a property use the following syntax:

[ "public" | "private" | "protected" ] [ "final" ]
[ "static" | "transient" | "volatile" ]
data_type var_name [ = var_initializer ] ";"

The items in the first optional group indicate the 'visibility' or accessibility from other objects. public means visible everywhere (global). private indicates accessible only to this class and nested classes. protected means visible to this class or inherited (ie. extended) classes only. The default (keyword omitted) is friendly or visible within the current package (folder) only.

final indicates continuous retention and unchangeable after initial assignment (ie. it is read only or constant).

The third optional group indicates how long a value is retained in the variable. static indicates that the value is shared by all members of the class and exists for all runtime. Static members can be referenced without creating an instance of the class. transient prevents the variable from being transferred during a serial operation such as file i/o. volatile is used in threading to prevent overwrite issues.

The data_type is one of the primitive types listed in tutorial 2 and can be optionally initialized.

Many programmers choose to make all properties private and force access through accessors and mutators which can include validation steps.

Types of Methods

Class behaviour are represented in Java by methods. To declare a method use the following syntax:

[ "public" | "private" | "protected" ] [ "final" ]
[ "static" | "abstract" | "native" ]
return_data_type method_name "(" parameter_list ")"
"{"
// some defining actions
"}"

Accessibility keywords are the same as for properties. The default (ie. omitted) is package (aka friendly) or visible within the current package only.

static methods are shared and retained. abstract methods must be redefined on inheritance. native methods are written in C but accessible from Java.

The return_data_type defines the type of value that the calling routine receives from the object (the reply message in object terminology). It can be any of the primitive types or the reserved word void (default value) if no message is to be returned. The statement return variablename; is used to declare the value to be returned to the calling routine.

The parameter_list can contain from zero to many entries of datatype variablename pairs. Entries are separated by commas. Parameters are passed by value, thus upholding the encapsulation principle by not allowing unexpected changes or side effects. Object references (such as arrays) can also be passed. Some examples of method header parameter lists are:

public static void example1() { }
public static int add2(int x) { x+=2; return x; }
public static double example3(int x, double d) { return x*d; }
public static void example4(int x, int y, boolean flagger) { }
public static void example5(int arr[]) { } // note: this is an object

Constructor methods allow class objects to be created with fields initialized to values as determined by the methods' parameters. This allows objects to start with values appropriate to use (eg. salary set to a base level or employeeNumber set to an incrementing value to guarantee uniqueness). For our simple box class:

public Box() // default box is point
{length = 0; width = 0; height = 0;}
public Box(int l,int w,int h) // allows giving initial size
{length = l; width = w; height = h;}

Note that there is no class keyword or return datatype keyword. Also the method name is the same as the class name. This is what marks the fragment as a constructor method. If no constructor method is defined for a class, a default constructor is automatically used to initialize all fields to 0, false or unicode(0) as appropriate to the datatype. Constructors can be overloaded in the same way that instance methods are.

One clever programming device is to declare the constructor with no parameters as private and use it to initialize all properties. Then other constructors can first call it using this() and then do their own specific property validations/initialization.

Accessor (or observer) methods read property (ie field variable) values and are conventionally named getFoobar() or whatever the property is called.

Mutator (or transformer) methods set property values and are often named setFoobar() etc. Mutators can be used to ensure that the property's value is valid in both range and type.

It is good programming practice to include accessor and mutator methods for each property in the class. They are perfect example of object encapsulization. The exceptions to writing accessor/mutator methods for each property is for those that are used only within the class itself or for properties that are set in more complex ways.

Helper methods are those routines that are useful within the class methods but not outside the class. They can help in code modularization. Normally they are assigned private access to restrict use.

Overloading and Recursion

Overloaded methods are methods with the same name signature but either a different number of parameters or different types in the parameter list. For example 'spinning' a number may mean increase it, 'spinning' an image may mean rotate it by 90 degrees. By defining a method for handling each type of parameter you achieve the effect that you want.

Overridden methods are methods that are redefined within an inherited class.

Recursive methods are methods that are defined in terms of themselves. A classic recursion is factorials where n factorial is the product of n and all the products before it down to one. In Java this could be programmed as:

class Factorial
{
int factorial(int n)
{
if (n==1) {return 1};
return (n * factorial(n-1));
}
}

Object Creation and Destruction

To create an object of a particular class use the creation method after the particular class has already been specified. For example, now that there is a constructor for the Box class you can make specific instances or discrete copies of a box by using the assignment operator and the new memory allocation operator as in:

Box box_1 = new Box(3,4,5);

Note: Once a class has been created, a datatype exists with the same name.

You do not need to destroy or remove an object when it is no longer needed. Java automatically flags unused objects and applies garbage collection when appropriate. However you may occasionally need to use the finalize method to insure that a non-Java resource such as a file handle or a window font character is released first. The general form is:

void finalize()
{
//cleanup code goes here
}

Inner Classes

Inner classes are classes nested inside another class. They have access to the outer class fields and methods even if marked as private. Inner classes are used primarily for data structures, helper classes, and event handlers. A brief example of how an inner class can be used as a data structure is:

public class Main2
{
class Person
{
// inner class defines the required structure
String first;
String last;
}
// outer class creates array of person objects with specific properties
// the objects can be referenced by personArray[1].last for example
Person personArray[] = {new Person(), new Person(), new Person()};
}

Project Organization

It is standard programming practice to write separate files for the class templates and the driver or main user programs. This allows separate compilation as well as class reuse by other driver programs. A class file can contain more than one associated class but normally its filename is that of the first defined file. A driver program is named the same as the class that contains the main(). It may contain other classes as well.

Project: Circle Class

At this point we need a small project to test your ability to create a class and then use it to create an object that does something! Circle is a class with properties radius, area, and diameter and methods setRadius(), getRadius(), calcDiameter(), calcArea(). Use double precision for everything. For the mathematically challenged, the area of a circle is PI r squared (NO! pie are round, cake are square ;-] ;-] ). Use 3.14 for PI. Test with a value of three for the radius. The diameter should read as 6 and the area as 28.25999...

Since this is such a short program, you may want to use only one file say MakeCircle.java that contains both the main (or driver) and the Circle class. Once it is working, see if you can factor it into two files (Circle and MakeCircle) and compile separately, (the Circle class file first). As an enhancement, you may want to add a command line interpreter to get the radius for the driver to use.

2 comments:

Anonymous said...

Very nice detailed information... thanks for sharing dude!!!

Anonymous said...

Good post and this post helped me alot in my college assignement. Say thank you you on your information.