|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 5
For example, if later someone adds a 20 ounce cup to the offerings of the virtual café, it would then be possible to hold up to 592 milliliters (ml) of coffee in a cup. If a programmer adds the new cup size without realizing you are using 500 ml to indicate that a cup needs washing, it is likely that a bug will be introduced. If a customer in your virtual café bought a 20 ounce cup, then took a big 92-ml gulp, he or she would then have exactly 500 ml remaining in the cup. The customer would be shocked and dissatisfied when, after drinking only 92 ml, the cup disappeared from his or her hand and appeared in the sink, ready to be washed. And, even if the programmer making the change realized that you were using a special value, another special value for the unwashed attribute would have to be chosen.
A better approach to this situation is to have a separate field to model the separate attribute:
// In source packet in file fields/ex2/CoffeeCup.java
class CoffeeCup {
private int innerCoffee;
private boolean needsWashing;
public boolean isReadyForNextUse() {
// If coffee cup isn't washed, then it's
// not ready for next use
return !needsWashing;
}
public void setCustomerDone() {
needsWashing = true;
//...
}
public void wash() {
needsWashing = false;
//...
}
// ...
}
Here the innerCoffee field is used only to model the amount of coffee in the cup attribute. The cup-needs-washing attribute is modeled by the
needsWashing field. This scheme is more easily understood than the previous scheme, which used a special value of innerCoffee and wouldn't prevent someone from expanding the maximum value for innerCoffee.
Another rule of thumb to follow when creating fields is to use constants (static final variables) for constant values that are passed to, returned from, or used within methods. If a method expects one of a finite set of constant values in one of its parameters, defining constants helps make it more obvious to client programmers what needs to be passed in that parameter. Likewise, if a method returns one of a finite set of values, declaring constants makes it more obvious to client programmers what to expect as output. For example, it is easier to understand this:
if (cup.getSize() == CoffeeCup.TALL) {
}
than it is to understand this:
if (cup.getSize() == 1) {
}
You should also define constants for internal use by the methods of a class -- even if those constants aren't used outside the class -- so they are easier to understand and change. Using constants makes code more flexible. If you realize you miscalculated a value and you didn't use a constant, you'll have to go through your code and change every occurrence of the hard-coded value. If you did use a constant, however, you'll only need to change it where it is defined as a constant.
Constants and the Java compiler
A useful thing to know about the Java compiler is that it treats static final fields (constants) differently than other kinds
of fields. References to static final variables initialized to a compile-time constant are resolved at compile-time to a local
copy of the constant value. This is true for constants of all the primitive types and of type java.lang.String.
Normally, when your class refers to another class -- say, class java.lang.Math -- the Java compiler places symbolic references to class Math into the class file for your class. For example, if a method of your class invokes Math.sin(), your class file will contain two symbolic references to Math:
MathMath's sin() method
To execute the code contained in your class that refers to Math.sin(), the JVM would need to load class Math to resolve the symbolic references.
If, on the other hand, your code only referred to the static final class variable PI declared in class Math, the Java compiler would not place any symbolic reference to Math in the class file for your class. Instead, it would simply place a copy of the literal value of Math.PI into your class's class file. To execute the code contained in your class that uses the Math.PI constant, the JVM would not need to load class Math.
The upshot of this feature of the Java compiler is that the JVM doesn't have to work any harder to use constants than it does to use literals. Preferring constants over literals is one of the few design guidelines that enhances program flexibility without risking any degradation of program performance.
The remainder of this article will discuss method design techniques that are concerned with the data a method uses or modifies. In this context, I'd like to identify and name three basic types of methods in Java programs: the utility method the state-view method, and the state-change method.
The utility method
A utility method is a class method that doesn't use or modify the state (class variables) of its class. This kind of method
simply provides a useful service related to its class of object.
Some examples of utility methods from the Java API are:
Integer) public static int toString(int i) -- returns a new String object representing the specified integer in radix 10
Math) public static native double cos(double a) -- returns the trigonometric cosine of an angle
The state-view method
A state-view method is a class or instance method that returns some view of the internal state of the class or object, without
changing that state. (This kind of method brazenly disregards the Heisenberg Uncertainty Principle -- see Resources if you need a refresher on this principle.) A state-view method may simply return the value of a class or instance variable,
or it may return a value calculated from several class or instance variables.
Some examples of state-view methods from the Java API are:
Object) public String toString() -- returns a string representation of the object
Integer) public byte byteValue() -- returns the value of the Integer object as a byte
String) public int indexOf(int ch) -- returns the index within the string of the first occurrence of the specified character
The state-change method
The state-change method is a method that may transform the state of the class in which the method is declared, or, if an
instance method, the object upon which it is invoked. When a state-change method is invoked, it represents an "event" to a
class or object. The code of the method "handles" the event, potentially changing the state of the class or object.