Into Java - Part XI

From EDM2
Jump to: navigation, search
Into Java / Part
I II III IV V VI VII VIII IX X XI XII
XIII IV XV XVI XVII XVIII XIX XX XXI XXII XXIII

By Simon Gronlund

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?

The Factory Pattern

We actually have used the so called Factory Pattern, a pattern described in many theoretical papers. As in real life we make a call to the factory and it delivers the gods it usually produces. You usually have some choices that you include in your order. We have used the Box class: Box.createVerticalBox() where you can order a specific kind of a Box. If you look the class Box up in your Java API you will see that almost every method of Box is static, you do not need to instantiate an object, you merely order one from the class.

Another example is BorderFactory, also a factory, easily read from its name. In fact BorderFactory is more factory than Box is, from the theoretical Factory Pattern that is. The difference is that here you never instantiate an object of that class. From the Box class you got Box's, though vertical or horizontal. But the BorderFactory never returns a BorderFactory (it has no constructors in fact), but you instantiate subclasses to the Border super class, as BevelBorder is one we have used. You may use any of the methods it provides to get any kind of supported border you like to use. And BorderFactory will generously provide you with as many you need.

Compare this to the situation with human beings, both women and men are humans with many qualities in common. Hence it would be good to have a class called Human being in charge of the shared variables, as name, birth date, address, etc. But we will never instantiate a Human, as we prefer women or men. Thus we want this Human class to act as a factory that can return an object of the preferred sex.

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:

public Human getPerson(long ssNumber)

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.

Override a Method

Overriding a method is a OOP concept that you have to master. If not, you will soon be trapped and you will not understand why. Last time we used the method paintComponent, recall that we as the first line made a call like

super.paintComponent(g);

and 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 method 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

public void raiseSalary(float percentRaise) {
   float temp = getSalary();
   setSalary(100 + temp);
   super.raiseSalary(percentRaise);
}

We overrode the method 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

public void raiseSalary(float raise) {
   float oldSal = getSalary();
   setSalary(oldSal + raise);
}

Next time we will look into the OOP concept of overloading methods and polymorphism, as the partner to override a method.

The bugs

Last time I gave you home works to do, correct the bug of only being able to draw rectangles and ovals from upper left to lower right. Why is that the only way to draw such shapes? And I think you all passed the test, since the methods defined by Java API defines x and y to be the upper left corner and width and height to be positive numbers. So what to do then? A humble test if the width and the height is negative, and if so, correct it.

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.

PaintBox class

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.

Keep shapes in a memory

Last time I promised to answer the question, why is only one shape painted, and why is it lost when painting another one. The answer is of course that we never store the shapes anywhere. A new shape will use the same variables as the former did. We need to introduce some kind of store. This time we will use the class Vector found in the java.util package.

PaintShape class

Further we have to make ourselves another class, let us call it 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.

PaintBox class

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.

PaintPanel class

Naturally, we have to change the PaintPanel class as well, first to let it instantiate these new objects, then to have them stored. Please declare a Vector among the declared variables, since Vector gives us a good store

   private Vector storedShapes;

and the line

   storedShapes = new Vector();

within the class' constructor. Since 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);
    }
Picasso or not
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.

Summary

We have discussed the OOP concept of overriding methods:

  • To override a method is to make an exact copy of a method's head of a superclass or interface in subclasses but return subclassed objects disguised as the superclass or interface
  • You may make a call to super if it is appropriate--always done when using system superclasses

Further we have discussed the Factory pattern, how it can be used as a superclass defining common qualities but sub classes will add specific characteristics to it. Or a class containing methods to return different objects subclassing a common interface.

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.