Designing with Extensibility in Mind

Adding new features to a class might be as simple as extending an existing class, adding a few new methods, and modifying the behavior of others. It is not necessary to rewrite everything. This is where inheritance comes into play. If you have just written a Person class, you must consider the fact that you might later want to write an Employee class or a Vendor class. Thus, having Employee inherit from Person might be the best strategy; in this case, the Person class is said to be extensible. You do not want to design Person so that it contains behavior that prevents it from being extended by classes such as Employee or Vendor (assuming that in your design, you really intend for other classes to extend Person). For example, you would not want to code functionality into an Employee class that is specific to supervisory functions. If you did, and then a class that does not require supervisory functionality inherited from Employee, you would have a problem.

This point touches on the abstraction guideline discussed earlier. Person should contain only the data and behaviors that are specific to a person. Other classes can then subclass it and inherit appropriate data and behaviors.

Making Names Descriptive

Earlier we discussed the use of proper documentation and comments. Following a naming convention for your classes, attributes, and methods is a similar subject. There are many naming conventions, and the convention you choose is not as important as choosing one and sticking to it. However, when you choose a convention, make sure that when you create classes, attributes, and method names, you not only follow the convention, but also make the names descriptive. When someone reads the name, he should be able to tell from the name what the object represents. These naming conventions are often dictated by the coding standards at various organizations.

Making names descriptive is a good development practice that transcends the various development paradigms.

Abstracting Out Nonportable Code

If you are designing a system that must use nonportable (native) code (that is, the code will run only on a specific hardware platform), you should abstract this code out of the class. By abstracting out, we mean isolating the non-portable code in its own class or at least its own method (a method that can be overridden). For example, if you are writing code to access a serial port of particular hardware, you should create a wrapper class to deal with it. Your class should then send a message to the wrapper class to get the information or services it needs. Do not put the system-dependent code into your primary class (see Figure 5.5).

Image

Figure 5.5. A serial port wrapper.

For example, consider the situation when a programmer is interfacing directly with hardware. In these cases, the object code of the various platforms will most likely be quite different and thus code must be written for each platform. However, if the functionality is placed in a ‘wrapper’ class, then a user of the class can interface directly with the wrapper and not have to worry about the various low-level code. The wrapper class will deal with the differences in these platforms and decide which code to invoke.

Providing a Way to Copy and Compare Objects

Chapter 3 discussed the issue of copying and comparing objects. It is important to understand how objects are copied and compared. You might not want, or expect, a simple bitwise copy or compare operation. You must make sure that your class behaves as expected, and this means you have to spend some time designing how objects are copied and compared.

Keeping the Scope as Small as Possible

Keeping the scope as small as possible goes hand-in-hand with abstraction and hiding the implementation. The idea is to localize attributes and behaviors as much as possible. In this way, maintaining, testing, and extending a class are much easier.

For example, if you have a method that requires a temporary attribute, keep it local. Consider the following code:

public class Math {

    int temp=0;

    public int swap (int a, int b) {

        temp = a;
        a=b;
        b=temp;

        return temp;

    }

}

What is wrong with this class? The problem is that the attribute temp is needed only within the scope of the swap() method. There is no reason for it to be at the class level. Thus, you should move temp within the scope of the swap() method:

public class Math {

    public int swap (int a, int b) {

        int temp=0;

        temp = a;
        a=b;
        b=temp;

        return temp;

    }

}

This is what is meant by keeping the scope as small as possible.

A Class Should Be Responsible for Itself

In a training class based on their book Java Primer Plus, Tyma, Torok, and Downing propose the class design guideline that all objects should be responsible for acting on themselves whenever possible. Consider trying to print a circle.

To illustrate, let’s use a non-OO example. In this example, the print command is passed a Circle as an argument and prints it (see Figure 5.6):

print(circle);

Image

Figure 5.6. A non-OO example of a print scenario.

The functions print, draw, and others need to have a case statement (or something like an if/else structure) to determine what to do for the given shape passed. In this case, a separate print routine for each shape could be called:

printCircle(circle);
printSquare(square);

Every time you add a new shape, all the functions need to add the shape to their case statements:

switch (shape) {
    case 1:  printCircle(circle); break;
    case 2:  printSquare(square); break;
    case 3:  printTriangle(triangle);  break;
    default: System.out.println("Invalid shape.");break;
}

Now let’s look at an OO example. By using polymorphism and grouping the Circle into a Shape category, Shape figures out that it is a Circle and knows how to print itself (see Figure 5.7):

shape.print(); // Shape is actually a Circle
shape.print(); // Shape is actually a Square

Image

Figure 5.7. An OO example of a print scenario.

The important thing to understand here is that the call is identical; the context of the shape dictates how the system reacts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.26.158