![]() ![]() 16 November 2000 ![]() |
![]() |
Into Java, Part 11
Quite a few times we have used some different Object Oriented Programming
(OOP) techniques and I have not made a fuss about that. Now I will use a few minutes to explain two concepts used.
The first one is, how can you make yourself a factory that creates objects that you need? And is that important
to know? The second topic will be, what is a method overriding another one? What are the pitfalls, and what are
the benefits? |
In Sweden we use a person's social security number (it is much
more used than it need to be for integrity reasons) to find out a person's sex, and hence we can send that number
as a parameter to Human and we will receive an object of the correct sex. If we can provide a method that takes
the social security number as parameter and that will return a subclass to Human, a
Woman
or a Man
depending on the number:
This technique does not seem to be such a killer as it indeed is. The Factory pattern may be the perfect solution, not only to humans but to many other situations. Perhaps you would like to get something called PrinterObject, having a few methods taking print jobs. But depending on what printer is plugged to the computer or the network, different objects shall be returned to you, but with common interfaces; your factory in work.public Human getPerson(long ssNumber)
paintComponent
, recall that we as the first line made
a call likeand we said that it is a good practice to always do that call. Why? Since the class we worked with inherited from JPanel, that in fact inherited from JComponent that has a method with the same name. So, two generations down we are overriding the methodsuper.paintComponent(g);
paintComponent
with our own method and implementation.
If we are not doing that super.paintComponent(g)
the
"grandclass" will never receive any calls and hence can not update the stuff it is responsible for.
A veeery common mistake.But why not simply name our own method differently? Since the system
always looks for methods with certain names, in this case paintComponent
, but there are several other examples. Anyway, if you override a method
of the system, make sure you always do that call to super
, the ancestor of your class.
On the other hand, when creating your own hierarchy of classes
you will be the judge if you have to or not have to call any ancestor class when overriding methods. Think of
the class Human again, used in a company it is for pay-rolls and have a method raiseSalary(float
percentRaise)
. This method is useful for many employees
as you inherit Human to Receptionist, Clerk, Programmer etc. But Developer is treated specially, a raise is not
only a percentage raise but a minimum of $100 as well and the raise is these $100 plus the percentage raise computed
on that temporary amount. Hence we must first add 100 to the old salary and after that we call super
We overrode the methodpublic void raiseSalary(float percentRaise) { float temp = getSalary(); setSalary(100 + temp); super.raiseSalary(percentRaise); }
raiseSalary
, but made a call to super as well. And different from constructors you
do not need have this call as the first line (within constructors calls to super have to be the first line, when
such calls are used).But how to do with the Boss, that also inherits from Human? A Boss
never get a percentage salary raise, no it goes upwards step by step. Hence we may override the
raiseSalary
method but we do not do a call to super.
Boss simply implements the method as
Next time we will look into the OOP concept of overloading methods and polymorphism, as the partner to override a method.public void raiseSalary(float raise) { float oldSal = getSalary(); setSalary(oldSal + raise); }
But to my surprise i found that I had introduced another bug I
did not intend to. I beg your pardon. With the old version of PaintBox strange things happened to the shapes if
the window was repainted, as from resizing it or getting focus back from being hidden. The shapes almost disappeared,
or they really did. Why so? Because I did not think of that situation when modeling the method! What was the problem?
In the paintComponent
method
I used the stopX
and stopY
variables to be the width and
height. That worked as long as no more call was made to this method. But repainting a component will give another
call. Any new call to this method will again compute the width and height, but this time with trashed values in stopX
and
stopY
.
The solution is to use temporary variables for the width and height,
and have them recomputed every time a call is made to the paintComponent
method. And of course never ruin neither the start point or the stop point.
Thus we introduce startXX
and startYY
as temporary starting values,
since we sometimes must switch them with the upper left point as seen below. Further we introduce height
and
width
. The solution is told in the image below.
|
Is it then possible to outline the ovals while being painted? Yes,
of course. Simply declare a new class variable private boolean mousePressed
among the other declared class variables. Within the
MouseAdapter : mousePressed
method within
the class' constructor, add the sole line line mousePressed = true
. And add its counterpart mousePressed = false
within the mouseReleased
method, preferably as the first line of that method.
This boolean variable; and the toggling lines, will be used by the paintComponent
method.
To paintComponent
we will add these lines
if (mousePressed) {
g.drawRect(startXX, startYY, width, height);
}
first in the block handling the oval painting, after the "//
remaining is oval" comment. Hey presto, it works. But why this extra variable? Else the sole line drawRect ...
seen above will not
vanish after you have completed your task. But using this variable, it is set to false when the mouse button is
released, and the outline is not painted. This single boolean variable simply keeps track of whether a shape is
being painted or not, and when the shape shall be considered finished the outline is not painted.
You may easily crouch down under the overwhelming number of booleans
used in bigger applications. And under the careful planning needed to be sure how they will interact and how they
must not restrain legal actions.
Vector
found
in the java.util
package. PaintShape
, not to confuse ourselves
with the java.awt.Shape
.
This class will hold familiar values as the start and end points, shape, color and if filled. In fact it will
give as objects of each shape we paint.
|
But how will it ever be painted? Please copy the entire paintComponent
from the PaintPanel
class and paste it into
this class. Then remove the call to super. Why? Since this class is no subclass to anything (except Object as
any class is), it will simply paint itself, a shape. Further remove these newly added lines on
if mousePressed
from it.
Finally, change the tests to look like
if (shape == PaintPanel.LINE) { // that is line
and
if (shape == PaintPanel.RECTANGLE) { // that is rectangle
The class is now complete.
Vector
among the declared variables, since Vector
gives us a good store private Vector storedShapes;
storedShapes = new Vector();
Vector
is located in the
java.util
package we need to import that package as
well. This far we have made the basic steps to introduce this new object and made a store to use.Then continue within the MouseAdapter : mouseReleased
method and add some lines
PaintShape tempShape = new PaintShape(filledShape, paintColor, shape, startX, startY);
tempShape.setStop(stopX, stopY);
storedShapes.addElement(tempShape);
at the end, only the repaint()
call will remain the last line. When a user releases his mouse button after
painted a shape all these values are known to the application, hence this object can be instantiated. But this
object is not instantiated during the painting phase, the values are only used internally within the PaintPanel
object until the mouse is released. Next step is to add the tempShape
into the store (note that from Java 1.2 you may use
add(Object o)
).
So far we have a way to make ourselves shapes and add them to the
store, but still we cannot retrieve them. They vanish when we begin to paint another shape though they really
are stored in our vector. Hence we must change the recently changed paintComponent
of the PaintPanel
class. Each time this method is called, the panel is repainted and that
erases everything else upon the canvas. Thus we must force this method to also paint the objects stored in the storedShapes
object. Simply add these
lines at the second first line of the PaintPanel
's paintComponent
method, only the call
to super
shall come before
these lines
for(int i = 0; i < storedShapes.size(); i++) {
PaintShape tempObj = (PaintShape) storedShapes.elementAt(i);
tempObj.paintComponent(g);
}
Compile and go! The for loop will run as many turns as there are shapes
added to the vector, the size of the vector reflects the number of objects stored. We start with element zero
(why zero has a historic reason, based on the fact that the first element has zero offset from
the starting point). Hence, if there are five objects stored in a vector, the last element is located at index
four, as object one is located at index zero. Thus you may always ask for the size of the vector and run the loop
as long as
i
is less than
the size. If there is no element added yet, no turn within the loop is allowed, the test is always done before
you enter the loop.
A vector always stores Java Objects
, any kind is possible (except for data types since these are no Objects
but data types), and thus we
need to cast the elements to the proper type. This time we know, since we designed the code ourselves, that they
are of PaintShape
type
(later on we will look into how to do when we are not sure). This cast is one when we merely tell the compiler
what type it is to expect and to use.
The retrieved object is stored in a temporary local variable. Finally
the object's paintComponent
method
is called, using the parameter g
as
we are used to. But note, this time we could have given the painting method any name since it will never get a
call from the system. As long as we are making the calls ourselves, we may name the methods any name. But the
system uses specified names we have to stick to.
We have acquainted ourselves with
java.util.Vector
, a kind of store, or better word for
it is a Data Structure. We used two of its many methods, public void addElement(Object
obj)
and public Object
elementAt(int index)
. Used a for loop to get objects
out of the vector, casting and using them. Next time we will look into how a vector looks like under the hood,
as well as another similar data structure.
But we did not implement a good way to erase shapes from the canvas.
How can we do that? How to make it possible to click any shape and have it removed? The next time we will add
that feature to the application and then we have to discuss some other properties of Java. We will also discuss
the OOP topic of overloading, not that new to us but yet unexplained.
![]() |
|
|