| < Day Day Up > |
Why Encapsulation Is Fundamental to OO
Encapsulation is really the fundamental concept of OO. Whenever the interface/implementation paradigm is covered, we are really talking about encapsulation. The basic question is what in a class should be exposed and what should not be exposed. This encapsulation pertains equally to data and behavior. When talking about a class, the primary design decision revolves around encapsulating both the data and the behavior into a
Stephen Gilbert and Bill McCarty define encapsulation as "the process of packaging your program, dividing each of its classes into two distinct
But what does encapsulation have to do with inheritance, and how does it apply with regard to this chapter? This has to do with an OO paradox. Encapsulation is so fundamental to OO that it is one of OO design's cardinal rules. Inheritance is also
How Inheritance Weakens EncapsulationAs already stated, encapsulation is the process of packaging classes into the public interface and the private implementation. In essence, a class hides everything that is not necessary for other classes to know about.
Peter Coad and Mark Mayfield make a case that when using inheritance, encapsulation is
The problem is that if you inherit an implementation from a superclass and then change that implementation, the change
ripples through
the class hierarchy. This rippling effect
Figure 7.9. A UML diagram of the Cabbie class.
Keep Testing Even with encapsulation, you would still want to retest the classes that use Cabbie to verify that no problem has been introduced by the change. If you then create a subclass of Cabbie called PartTimeCabbie , and PartTimeCabbie inherits the implementation from Cabbie , changing the implementation of Cabbie directly affects the PartTimeCabbie class.
For example, consider the UML diagram in Figure 7.10.
PartTimeCabbie
is a subclass of
Cabbie
. Thus,
PartTimeCabbie
inherits the public implementation of
Cabbie
, including the method
giveDirections()
. If the method
giveDirections
is changed in
Cabbie
, it will have a direct impact on
PartTimeCabbie
and any other classes that might later be subclasses of
Cabbie
. In this subtle way, changes to the implementation of
Cabbie
are not
Figure 7.10. A UML diagram of the Cabbie/PartTimeCabbie classes.
To reduce the risk posed by this dilemma, it is important that you stick to the strict is-a condition when using inheritance. If the subclass were truly a specialization of the superclass, changes to the parent would likely affect the child in ways that are natural and expected. To
How can inheritance be used improperly? Consider a situation in which you want to create a window for the purposes of a graphical
class Rectangle {
}
class Window extends Rectangle {
}
In reality a GUI window is much, much more than a rectangle. It is not really a specialized version of a rectangle, as is a square. A true window might contain a rectangle (in fact many rectangles); however, it is really not a true rectangle. In this approach, a Window class should not inherit from Rectangle , but it should contain Rectangle classes.
class Window {
Rectangle menubar;
Rectangle statusbar;
Rectangle mainview;
}
A Detailed Example of PolymorphismMany people consider polymorphism the cornerstone of OO design. Designing a class for the purpose of creating totally independent objects is what OO is all about. In a well-designed system, an object should be able to answer all the important questions about it. As a rule, an object should be responsible for itself. This independence is one of the primary mechanisms of code reuse.
As stated in Chapter 1, polymorphism literally means
many
To review the example in Chapter 1, consider a class called
Shape
. This class has a behavior called
Draw
. However, when you tell somebody to draw a shape, the first question they ask is likely to be, "What shape?" Simply telling a person to draw a shape is too abstract (in fact, the
Draw
method in
Shape
contains no implementation). You must specify which shape you mean. To do this, you provide the actual implementation in
Circle
and other subclasses. Even though
Shape
has a
Draw
method,
Circle
Object Responsibilty
Let's
Figure 7.11. The Shape class hierarchy.
Polymorphism is one of the most elegant uses of inheritance. Remember that a
Shape
cannot be
However, Rectangle and Circle can be instantiated because they are concrete classes. Rectangle and Circle are both shapes; however, they obviously have some differences. Because Rectangle and Circle are both shapes, their area can be calculated. Yet, the formula to calculate the area is different for each. Thus, the area formulas cannot be placed in the Shape class.
This is where polymorphism comes in. The
As a very simple example, imagine that there are four classes: the abstract class Shape , and concrete classes Circle , Rectangle , and Star . Here is the code:
public abstract class Shape{
public abstract void draw();
}
public class Circle extends Shape{
public void draw() {
System.out.println("I am drawing a Circle");
}
}
public class Rectangle extends Shape{
public void draw() {
System.out.println("I am drawing a Rectangle");
}
}
public class Star extends Shape{
public void draw() {
System.out.println("I am drawing a Star");
}
}
Notice that there is only one method for each class:
draw
. Here is the important point regarding polymorphism and an object being responsible for itself: The concrete classes themselves have responsibility for the drawing function. The
Shape
class does not provide the code for drawing; the
Circle
,
Rectangle
, and
Star
classes do this for themselves. Here is some code to
public class TestShape {
public static void main(String args[]) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Star star = new Star();
circle.draw();
rectangle.draw();
star.draw();
}
}
Compiling This Code If you want to compile this Java code, make sure that you set classpath to the current directory: javac -classpath . Shape.java javac -classpath . Circle.java javac -classpath . Rectangle.java javac -classpath . Star.java javac -classpath . TestShape.java Actually, when you compile a Java class (in this case TestShape ) and it -requires another class (let's say Circle ), the javac compiler will attempt to compile all the required classes. Thus, the following line will actually compile all the files in this example. javac -classpath . TestShape.java The test application TestShape creates three classes: Circle , Rectangle , and Star . To actually draw these classes, TestShape simply asks the individual classes to draw themselves: circle.draw(); rectangle.draw(); star.draw(); When you execute TestShape , you get the following results: C:\>java TestShape I am drawing a Circle I am drawing a Rectangle I am drawing a Star This is polymorphism at work. What would happen if you wanted to create a new shape, say Triangle ? Simply write the class, compile it, test it, and use it. The base class Shape does not have to change ”nor does any other code:
public class Triangle extends Shape{
public void draw() {
System.out.println("I am drawing a Triangle");
}
}
A message can now be sent to Triangle . And even though Shape does not know how to draw a triangle, the Triangle class does:
public class TestShape {
public static void main(String args[]) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Star star = new Star();
Triangle triangle = new Triangle ();
circle.draw();
rectangle.draw();
star.draw();
triangle.draw();
}
}
C:\>java TestShape
I am drawing a Circle
I am drawing a Rectangle
I am drawing a Star
I am drawing a Triangle
To see the real power of polymorphism, you can actually pass the shape to a method that has
public class TestShape {
public static void main(String args[]) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Star star = new Star();
drawMe(circle);
drawMe(rectangle);
drawMe(star);
}
static void drawMe(Shape s) {
s.draw();
}
}
In this case, the Shape object can be passed to the method drawMe, and the drawMe method can handle any valid Shape ”even one you add later. You can run this version of TestShape just like the previous one. |
| < Day Day Up > |