Encapsulating Object Creation Polymorphism enables code to
be more abstract. When your code references an interface instead of a class, it
loses its coupling to that class and becomes more flexible in the face of
future modifications. This use of abstraction was central to many of the
techniques of the previous chapters. Class constructors are the one place where
such abstraction is not possible. If you want to create an object, you need to
call a constructor; and calling a constructor is not possible without knowing
the name of the class. This chapter addresses that problem by examining the techniques
of object caching and factories. These techniques help the designer limit
constructor usage to a relatively small, well-known set of classes, in
order to minimize their potential liability.
Object Caching
Suppose you want to write a program that analyzes the status
of a large number of motion-detecting sensors, whose values are either “on” or
“off.” As part of that program, you write a class Sensors that stores the
sensor information in a list and provides methods to get and set individual
sensor values.
public class Sensors
{
private List
public Sensors(int
size)
{
for (int i=0; i
{
L.add(new Boolean(false));
}
public boolean getSensor(int n)
{
Boolean val =
L.get(n);
return val.booleanValue();
}
public void setSensor(int n, boolean b)
{
L.set(n, new Boolean(b));
}
}
This code creates a lot of Boolean objects: the constructor
creates one object per sensor and the setSensor method creates another object
each time it is called. However, it is possible to create far fewer objects.
Boolean objects are immutable (that is, their state cannot be changed), which
means that Boolean objects having the same value are indistinguishable from
each other. Consequently, the class only needs to use two Boolean objects: one
for true and one for false. These two objects can be shared throughout the
list.
Following code shows a revision of Sensors that takes
advantage of immutability. This code uses the variables off and on as a cache.
When it needs a Boolean object for true it uses on; and when it needs a Boolean
object for false it uses off.
public class Sensors
{
private List
private static final Boolean off = new Boolean(false);
private static final
Boolean on = new Boolean(true);
public Sensors(int size)
{
for (int i=0; i
{
L.add(off);
}
public boolean getSensor(int n)
{
Boolean val =
L.get(n);
return val.booleanValue();
}
public void setSensor(int n, boolean b)
{
Boolean val = b ? on : off; L.set(n, val);
}
}
This use of caching is a good idea, but in this case it is
limited to the Sensors class. If you want to use Boolean objects in another
class, it could be awkward to share the cached objects between the two classes.
Fortunately, there is a better way—the Boolean class has caching built into it.
Singleton Classes
One important use of caching is to implement singleton
classes. A singleton class is a class that has a fixed number of objects,
created when the class is loaded. It does not have a public constructor, so no
additional objects can be created. It is called “singleton” because the most
common situation is a class having a single instance. For example, if the Java
designers had made the Boolean constructor private (which would have been a
good idea) then Boolean would be a singleton class. On the other hand, Integer
cannot be a singleton class, even if its constructor were private, because its
valueOf method creates new objects when needed. The Java enum syntax simplifies
the creation of singleton classes and is the preferred way of writing
singletons.
Writing Boolean as an Enum
public enum Boolean
{
TRUE(true),
FALSE(false);
private boolean value;
private
Boolean(boolean b)
{
value = b;
}
public boolean booleanValue()
{
return value;
}
public static Boolean valueOf(boolean b)
{
return (b ? TRUE : FALSE);
}
...
}
Note that the syntactic differences are incredibly minor.
The main difference concerns the definitions of the constants TRUE and FALSE,
which omit both the declaration of their type and their call to the Boolean
constructor. The values inside the parentheses denote the arguments to the
constructor. That is, the statement TRUE(true), FALSE(false); is equivalent to
the two statements
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
Conceptually, an enum is a class that has no public
constructors, and therefore no objects other than its public constants. In all
other respects an enum behaves like a class.
Beginners are often unaware of
the correspondence between enums and classes because an enum is
typically introduced as a named set of constants. For example, the following
enum defines the three constants
Speed.SLOW, Speed.MEDIUM, and Speed.FAST:
public enum Speed {SLOW, MEDIUM, FAST};
This enum is equivalent to the class definition of the
following code. Note that each Speed constant is a reference to a Speed object
having no functionality of interest.
public class Speed
{
public static final Speed SLOW = new Speed();
public static final Speed MEDIUM = new Speed();
public static final
Speed FAST = new Speed();
private Speed() { }
}
As with classes, an enum constructor with no arguments and
no body (such as the constructor for Speed) is called a default constructor.
Default constructors can be omitted from enum declarations just as they can be
omitted from class declarations. Because the constants in an enum are objects,
they inherit the equals, toString, and other methods of Object. In the simple
case of the Speed enum, its objects can do nothing else. The elegant thing
about the Java enum syntax is that enum constants can be given as much
additional functionality as desired. The default implementation of an enum’s
toString method is to return the name of the constant. For example, the
following statement assigns the string “SLOW” to variable s.
String s = Speed.SLOW.toString();
0 Comments:
Post a Comment
If you have any doubts . Please let me know.