Wednesday, February 22, 2012

Choosing good abstractions

Coupling and cohesion
The efficiency of the programs you design depends on how well you choose the classes they use.

Well-abstracted classes lead to programs that are easy to maintain and extend. However, if you choose bad abstractions, you could end up with an error-prone application that doesn't model the problem domain effectively.
Simplicity is the key to choosing good classes.

The classes you choose should capture only one key abstraction. If a class performs two functions, then you should split it into two separate classes, each with its own function.
There are five key concepts you should consider when choosing classes:
  • coupling
  • cohesion
  • sufficiency
  • completeness
  • primitiveness
These allow you to measure how well-designed classes are.
Coupling refers to how tightly connected classes are. If a class is very dependent on other classes to carry out its functions, then it is strongly coupled.
Strongly coupled classes can lead to problems in program design. Because such classes are interdependent, they are difficult to extend, and bugs in one can affect others.

This interdependency can also make it difficult for other programmers to understand programs that contain such classes.
To perform adequately, classes must be connected to other classes. But you should choose a level of connection that doesn't make one class too dependent on another.

This is particularly important when you design for inheritance.
Though inheritance is a form of coupling, it can make programs easier to design and extend.

So when choosing classes, you need to balance the requirements of inheritance against the need for weak coupling.
Cohesion is a measure of how closely related the elements in a class are. These elements are the states, behaviors, and functionalities of the class.
A class whose members are simply grouped together and have little in common is only coincidentally cohesive. Such a class might have two unlinked functions, or it may group object states with unrelated object behavior.

Because its elements do not cohere to realize a single purpose, such a class is confusing, difficult to use, and often too complex to implement.
Let's say you used a class named BankEmployee to model how bank staff are paid and how they interact with customers.

Since the elements of these separate functionalities are unrelated, this class would be a bad abstraction.
Classes with good cohesion are well defined and contain elements that properly belong together.

Such classes are easy to understand and use because they serve a definite purpose – they have functional cohesion.

Sufficiency, completeness, and primitiveness
Sufficiency, completeness, and primitiveness are related principles.

A good class needs to balance the requirements of each of these principles before it can function well.
  • Sufficiency
  • Completeness
  • Primitiveness
Sufficiency
Sufficiency is the characteristic that ensures classes are able to function effectively.

You must create classes that contain all the attributes and operations necessary to perform the tasks you require of them. So if you create a Car class, you must include an accelerate operation – otherwise no car object will be able to change speed.

Sufficiency means that you should include only those attributes and operations that are needed for the system you are modeling.

If you want to create a class that models a typical car, you need to specify things like its speed and model.

But you don't need to specify what type of suspension the car uses – this would complicate the class without adding any important functionality to it.

Whether a class is sufficient or not depends on its purpose and on the level of detail it is intended to capture.

If you need to represent something in great detail, then you might have to include specifications that would not be needed in a more abstract model.
Completeness
Completeness determines whether the interface of the class captures all the behavior of the class.

The attributes and operations you give to a class must be general enough to allow other classes to communicate with it.
Primitiveness
Primitiveness means that you shouldn't make a class unnecessarily complicated. It is a principle that lets you control a large spectrum of behavior with just a few operations.

For any class, you should choose operations and attributes that are very basic.

To create more complex class states, you can simply change the values of the attributes you have chosen.

Or you can combine simple operations to create complex ones.

Let's say that in the Car class, you want to model the way gear changes work.

A vertical rectangle with three compartments depicts the Car class with the class name, Car, in the first compartment. The second compartment contains the attributes which are: engineSize, color, model, maxSpeed, currentSpeed. The third compartment contains the operations which are: accelerate(), brake(), turn(), getSpeed(), shiftUp(), shiftDown(), shiftUpTwoGears().

You will need to create a shiftUp operation, but you won't need a shiftUpTwoGears operation. You can simply use the more primitive shiftUp operation twice.

Primitiveness is very closely related to completeness.

To make a class complete, you need to account for all its behavior. But by defining this with primitive functions, you ensure that you don't have to list every possible permutation of a class's behavior.

The level of primitiveness you choose depends on how you want your program to perform.

If you need a program to run very fast, it may be better to use a complex function once rather than using a primitive one several times.

Summary

Classes should capture only one key abstraction. Classes that depend on other classes to function are strongly coupled. You should choose classes with weak coupling, because this makes programs easier to extend, maintain, and debug. Classes should also be functionally cohesive.

To be sufficient, a class must have enough attributes and operations to capture all the relevant states and behavior of its objects. You should make the set of these properties sufficiently complete to allow the purpose of the class to be easily understood, but you should also try to keep the class as simple as possible. You can do this by observing the principle of primitiveness.

Objects, classes, and UML

Classes in UML
A class is represented in UML by a rectangle with three compartments. These are
  • Class
  • attributes
  • operations()
Class
The first compartment in the UML diagram contains the class name, which UML recommends is centered and in bold. It also contains general properties like package owner or stereotype.
attributes
The second compartment contains the class's attributes. Attributes are features within a classifier that describe a range of values that instances of the classifier may hold.
operations()
The third compartment contains the class's operations. Operations are services that can be requested from an object that affect its behavior.
UML recommends that the names of both attributes and operations are left-justified and in plain type.
When representing a class, you can choose to show
  • attributes but not operations
  • operations but not attributes
  • attributes and operations
Or if you don't want to show any of a class's attributes or operations, you can simply represent it with a plain rectangle.
The names you choose for classes should come directly from the vocabulary of the problem domain. This helps you to understand what each class should do, and how it should relate to other classes.

If you have difficulty in naming a class, this can mean that it is poorly defined, or serves no useful purpose and should be discarded.
The following is a standard UML convention for naming classes.
Class names should
  • be singular nouns
  • start with an uppercase letter
Although not a UML recommendation, if you need to give a class a name that uses more than one word, you should
  • push the words together
  • capitalize the initial letter of each word
There are no strict UML conventions for naming attributes and operations. But it is common practice to push together words in any attribute or operation names that use more than one word.

The initial letter of the first word in such names is not usually capitalized, but the initial letter of each word after the first is.


Objects in UML
The UML notation for objects is very similar to the notation used for classes. Just like a class, an object is represented with a rectangle – with or without compartments.
But in contrast to a class name, an object name is always
  • underlined
  • in plain type
An object name can include the name of the class to which it belongs. In this case the class name follows the object name, and the two are separated by a colon.

For example, suppose the object BankTeller belongs to the Employee class. So its object name is bankTeller:Employee.
You can represent an object whose name or particular identity is irrelevant or not specified.
You do this by
  • underlining the class name
  • putting a colon before it
In a payroll system for a bank, it wouldn't be necessary to specify whether objects involved with the system were tellers or managers.

Such objects could be represented anonymously, with just their class name. So you would name each of them :Employee.


Class and object diagrams
Class diagrams are one of the most important types of diagram in UML.

This is because they provide a clear view of the relationship between a proposed system and its problem domain. Also, they can be used in conjunction with many development tools to generate skeleton code (the definition of the classes).
Class diagrams show only the static structure of systems.
They can be used to show
  • single classes
  • large groups of classes
  • relationships between classes
But they do not show the dynamic behavior of classes or the sequence of steps needed for a class to complete a use case.
Class diagrams can depict various levels of abstraction in a system.

They can give a high-level view by showing packages, which are groups of related classes handling specific parts of system functionality.
Or they can focus on the precise details of a single class's attributes and operations.
Class diagrams show a general overview of a system.
Object diagrams are very similar to class diagrams – but they provide a view of what a system looks like at a particular point in time, when classes are instantiated. They are useful for showing concrete examples of how a system functions.

Summary

Objects and classes are both represented in UML by a rectangle. A class has three compartments in the rectangle to show its name, attributes, and operations. An object has two compartments: one for the name and one for the attributes and their values.

Class diagrams show the static structure of classes, while object diagrams depict how classes look when instances are created. Class diagrams can show single classes, relationships between classes, and packages.

In UML, it is recommended, but not mandatory, that class names should be in bold, be without spaces, and have the initial letters of each word capitalized. Object names should be underlined and in plain type.

State, behavior, and identity

Objects
Much of the power and flexibility of modern software analysis and design derives from its use of objects.

The use of objects allows designers to create programs that are more easily maintained and extended. And it makes it easier to design very complex or large-scale programs.
The advantage of using objects in programming is that they allow us to model the real world more accurately.

Procedural programming represents the world as a series of processes acting on data.
However, object-oriented programming represents it the way we really see it – as a set of objects interacting with each other.
Use cases and problem statements allow you to define the problem domain you're analyzing.

After use cases and problem statements have been created, you need to model how the problem domain actually behaves.
By using objects, you can translate features of the problem domain into conceptual models that can be used to create software.

So creating objects is usually the next stage in a design project after use cases or problem statements have been drawn up.
An object is a distinct entity that represents something significant in the problem domain.

It has clear boundaries. And it has a well-defined behavior and a definite purpose.


Classes
Classes are the moulds or templates for the creation of objects. They set out rules for how the objects that they contain may behave.
Classes are part of the abstract structure of a program, and are created when the program is created.

But objects are created only when a program runs. In contrast to classes, they exist at a definite point in time.
Grouping objects into a class allows you to simplify the problem you're modeling.

Classes create an abstract representation of the world, letting you discard unnecessary details.
A class includes objects with similar
  • properties
  • behavior
  • relationships to other objects
The way you group objects into classes depends on the problem you're modeling.

Different problem domains have different requirements, even when they contain the same objects. You will need to create specific classes that suit the needs of each problem domain.
For example, the following are objects relevant to a bank application domain:
  • bank teller
  • manager
  • customer
  • ATM
  • Computer
  • mainframe
To model different aspects of the bank, you need to create classes that will capture the essential features of the problem domain.

So a class named ComputerSystem will capture the essential features of ATMs, computers, and the bank mainframe.
Alternatively, you could create separate classes for each of these objects if you wanted to analyze the problem domain in more detail.
Let's say you want to model the way users interact with the bank computer system.

You can create a class to model the behavior of bank tellers, customers, and managers. And all of these things will be instances of the ComputerSystemUser class.
You may need to rearrange the classes you've chosen if you want to model other aspects of the bank. For example, you might create a class named Employee to model the roles of staff in the bank.
But you would need to split this into an Employee class and a class named Manager if you also wanted to model the bank's chain of command.


State
All objects have three essential features:
  • state
  • behavior
  • identity
An object's state is defined by the attributes of the object and by the values these have.
An attribute is a feature of an object, which distinguishes it from other kinds of objects. For example, one of the attributes of any car object is that it is capable of movement – it has a speed.
An attribute is usually something static. This means that it cannot be removed or altered without changing the essential nature of the object to which it belongs.

For example, a car that could never be moved would not be a typical car.
Though attributes themselves are usually static, the value an attribute can have is usually dynamic.

So you can alter the state of an object by changing the value of its attributes.

Behavior and identity
You can model the states of a system by defining the states of the objects that represent it. But to capture the complexity of real world problems, you also need to consider how these objects behave.
Objects always exist in some kind of relation to other objects.

Sometimes they act on other objects, or even on themselves. And sometimes they are acted on by other objects.
The ways in which an object behaves are determined by its operations. So when an object needs to act on another object – to retrieve data, for instance – it uses an operation.
An operation occurs when a message is passed between two objects, allowing some function to be performed.
For structural and security reasons, some operations and attributes in a class or object are not visible to other classes.

This protects a program against malicious damage. And it prevents data being accidentally deleted or overwritten by other parts of the computer program.
The hidden parts of a class can be accessed only indirectly, through the visible parts of the class.

Together, the visible operations and attributes of a class make up its interface. This is the part of a class that is acted on by operations from other classes or system users.

The three most common types of operations that a class can have are
  • modifiers
  • selectors
  • iterators
modifiers
A modifier operation alters the state of an object.
selectors
A selector operation lets you access the state of an object without changing it.
iterators
An iterator operation allows all the parts of an object to be accessed in a definite order. Object-oriented programmers often create a separate class that is responsible for using the iterator function on an object.
Modifiers, selectors, and iterators are not part of any programming language, but are used to characterize the effects operations have.
Two types of operation are needed to create and destroy instances of a class. These are:
  • Constructor
  • Destructor
Constructor
The constructor operation creates an object and fixes its initial state.
Destructor
The destructor operation destroys an object.
Together, state and behavior define the roles that an object may play. And an object may play many roles during its lifetime.

For example, an object in the bank's Employee class could be involved with the payroll system, with the customer database, or with the command hierarchy.
The functions you require an object to perform are its responsibilities. And an object's responsibilities are fulfilled by its roles – by its state and behavior combined.

So you can capture all the meaningful functionality of an object by specifying its state and behavior.
Objects are characterized by a third feature in addition to state and behavior – identity.

Even objects with the same properties and behavior have their own individual identity. For instance, two blue station wagons that were built in the same year by the same manufacturer are still separate and unique cars.
The identity of an object is independent of its attributes or operations. So an object will retain its identity no matter what values its properties have.

You can, for example, respray your car or fit another engine, and it will still be the same car.

Summary

Objects are representations of abstract or real things. Their use in object-oriented programming is to allow designers to model the world accurately.

Objects represent particular instances of things, and classes represent types of object. Different classes are used for different problem domains.

States are the conditions in which objects exist. An object's state is defined by its attributes. An object's attributes are usually static, and the values of the attributes are usually dynamic.

The term "behavior" refers to how objects interact with each other, and it is defined by the operations an object can perform. There are five kinds of operations: modifiers, selectors, iterators, constructors, and destructors. No matter what its attributes and operations are, an object is always uniquely itself. It retains its identity regardless of changes to its state or behavior.

Thursday, February 16, 2012

Runtime and checked exceptions

Java packages contain Exception subclasses that describe exceptions that are specific to each package. Each subclass of Exception represents a particular exception type.
The RuntimeException class - a subclass of Exception - and its subclasses describe exception objects that are thrown automatically by the Java Virtual Machine (JVM) at runtime.

Runtime exceptions are generally caused by bugs in the source code. For instance, you need to ensure that a divisor is not equal to zero before dividing by it.
The RuntimeException subclass contains various subclasses. These include
  • ArithmeticException
  • IndexOutOfBoundsException
  • IllegalStateException
  • NegativeArraySizeException
ArithmeticException
When a program tries to do something that breaks the rules of arithmetic, an ArithmeticException is thrown. For example, if a method tries to divide an integer by zero, the method throws an instance of this class.
IndexOutOfBoundsException
When an index to a string or an array is out of range, an IndexOutOfBoundsException is thrown. For example, trying to access the twelfth element of a ten element array will throw this exception.
IllegalStateException
When a method has been invoked illegally an IllegalStateException is thrown. In other words it was not in the correct state to be called at the time.
NegativeArraySizeException
If an application tries to create an array that has negative size, a NegativeArraySizeException is thrown. An array must have zero or more elements.
You can catch RuntimeException types by using try-catch blocks.
Code that might trigger a runtime exception, but that does not contain try-catch blocks, will still compile. All exceptions deriving from RuntimeException are known as unchecked exceptions. This means that the compiler does not check whether they are handled or declared.
All the classes that inherit from Exception and are not RuntimeException subclasses are known as checked exception classes.
When checked exceptions might occur in a method, they must either be declared in the method declaration using a throws clause, or explicitly handled in the method body. Otherwise, the code will not compile.

An example of a situation that can cause a checked exception is when a method attempts to open a file that cannot be opened.

You use a try-catch statement to handle checked exceptions that are raised.
Checked exception classes include
  • ClassNotFoundException
  • InterruptedException
  • IllegalAccessException
  • FileNotFoundException
ClassNotFoundException
ClassNotFoundException is thrown when a program tries to load a class using its name, but the definition for the class with the specified name is not found. These exceptions occur when a program uses the forName method in the Class class or the findSystemClass or loadClass method of the ClassLoader class.
InterruptedException
When a thread has been inactive for a long time and another thread uses the interrupt method in the Thread class to interrupt it, an InterruptedException is thrown.
IllegalAccessException
When the currently executing method does not have access to the definition of a field the application is trying to get or set, an IllegalAccessException is thrown. This also applies if the method doesn't have access to the definition of a method the application is trying to invoke.
FileNotFoundException
FileNotFoundException, which belongs to the java.io package, is thrown when an application fails to open a file specified by a pathname. This can happen if the file is inaccessible, or if it doesn't exist.

The Throwable class

All Java exceptions and errors are subclasses of a class in the java.lang package called Throwable. Only an object of type Throwable can be thrown in code, including exceptions and system errors.
Methods of the Throwable class include
  • getMessage
  • toString
  • initCause
  • printStackTrace
getMessage
The getMessage method returns an appropriate String error message from a Throwable object.

The getMessage method returns null if the object was not created with an error message. You should provide descriptive messages for all exceptions handled in code.
toString
The toString method returns a description of an exception object, which includes the exception type.
initCause
The initCause method sets the cause of the exception, which is always another Throwable object. This method enables exception chaining.
printStackTrace
You use the printStackTrace method to find out which method has thrown a particular exception.
Suppose you want to print out the call stack at the point that e is caught.
public class TestExceptions2 {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ ) 
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    catch (ArrayIndexOutOfBoundsException e) {
      System.out.println ("Array bounds exceeded" ) ;
      MISSING CODE();
      wasError = true ;
    }
  }
}
To print out the call stack, you type e.printStackTrace.
The call stack records the series of method calls leading to the exception. When debugging, you can use printStackTrace to help determine where the exception originated in your code.
public class TestExceptions2 {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ ) 
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    catch (ArrayIndexOutOfBoundsException e) {
      System.out.println ("Array bounds exceeded" ) ;
      e.printStackTrace();
      wasError = true ;
    }
  }
}
The two immediate subclasses of Throwable are
  • Error
  • Exception
Error
Error and its subclasses are used for serious system or compile-time errors that cannot or should not be handled by an application. For instance these could include the following errors - ExceptionInInitializerError, StackOverflowError, and NoClassDefFoundError.
Exception
Exception and its subclasses are used for implementation-specific exceptions that an application might be expected to handle - for example, if a printer is switched off when the user attempts to print a document.

Exception is the superclass of all the exceptions you can handle in your code.

Wednesday, February 15, 2012

Using try, catch, and finally blocks

Java's exception-handling code is specified within a try...catch...finally block.


The try block encloses the code that might cause an exception to occur. The code in the try block is called protected code.
// basic try statement syntax
try {
  // protected code
}
catch (ExceptionType1 Identifier1) {
  // exception-handling code
}
catch (ExceptionType2 Identifier2) {
  // exception-handling code
}

finally {
  // 0 or 1 finally clause
}
You use zero or more catch blocks. When present, catch blocks specify exception handlers for the types of exceptions thrown. If no exception is thrown, then the code in the catch block doesn't run because it isn't needed.
The finally block is optional if a try block already has an associated catch block. If you have a finally block, the code it contains always executes, regardless of whether exceptions are thrown or not. The only exception to this rule is if a System.exit occurs, in which case the application terminates without executing a finally block.
If an exception occurs in a try block and is not caught in a catch block, a finally block will execute, provided it is present. The application then terminates.

If required, you must explicitly throw the exception up the call stack. If no method handles the exception, the program terminates when the exception object reaches the top of the call stack.
Consider the code in which the tryValues method contains appropriate exception-handling code.

The method takes two parameters – x and y. It declares and initializes an array and then prints the value of each array element divided by y, within a loop.
public class TestExceptions {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ )
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    //...
You enclose any lines of code that could cause exceptions in a try block.

For example, an exception could be caused if x is larger than the size of the array, or y is equal to zero.
public class TestExceptions {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ )
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    //...
If an exception occurs in a try block, execution is immediately directed to a series of catch blocks following the try block, which include the error-handling code.

Generally, you include a catch block for each type of exception that might be thrown in the try block, although you can write a single catch block for all exceptions if you like. You should aim to catch and handle specific exceptions, rather than general ones.
try {
  for (int i = 0; i <= x - 1; i++) {
    System.out.println (intArray[i] / y) ;
  }
}
catch (ArrayIndexOutOfBoundsException e) {
  System.out.println ("Array bounds exceeded" ) ;
  wasError = true ;
}
catch (ArithmeticException e) {
  System.out.println ("Attempt to divide by zero" ) ;
  wasError = true ;
}
catch (Exception e) {
  System.out.println ("Unknown exception occurred: " + e) ;
  wasError = true ;
}
finally {
  if (wasError)
    System.out.println ("Ending tryValues with error" ) ;
  else
    System.out.println ("Ending tryValues without an error" ) ;
}
If an exception occurs, the catch blocks are checked in order, from top to bottom.

If the exception is of the appropriate type for one of the catch blocks, the statements within the catch block are executed and no further catch blocks are checked. So the order in which you position catch blocks is important.

Introducing exception handling

In programming terms, an exception is an event that stops the normal execution of a program.

For example, a program might try to access an element outside the bounds of an array, or some file input and output operation might fail.

You need to ensure that your Java programs can deal with exceptions without simply crashing.
When an exception occurs at run time, the method in which it occurs creates an exception object. Execution is aborted unless the exception is handled somewhere along the call stack.

The method can throw the exception back to the calling method, which may be better able to handle it. The exception object includes information about the exception, such as the method in which it occurred and the cause of the exception.
The calling method can, in turn, throw the exception back to its caller. It's up to the developer to provide exception-handling code within the appropriate methods.
If no method handles the exception, the program terminates when the exception object reaches the top of the visible call stack - the main method.
When an exception occurs, you can prevent it being passed up the call stack – and potentially terminating the application – by providing handling code for the particular exception type, enabling the application to continue execution along a different path.

You can direct an application to use an exception handler in the method in which the exception occurs, or in one of that method's calling methods.
Once the exception is handled, the program continues to run, although this depends on the specific implementation.