Jump to content

Into Java - Part XI: Difference between revisions

From EDM2
Ak120 (talk | contribs)
No edit summary
Ak120 (talk | contribs)
mNo edit summary
Line 4: Line 4:
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?
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===
==The Factory Pattern==
<font size="-1" face="Helv,Helvetica,Arial">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:</font><font size="-1"><tt> Box.createVerticalBox() </tt></font><font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
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: <tt>Box.createVerticalBox()</tt> 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.


<font size="-1" face="Helv,Helvetica,Arial">Another example is</font><font size="-1"><tt> BorderFactory</tt></font><font size="-1" face="Helv,Helvetica,Arial">, also a factory, easily read from its name. In fact</font><font size="-1"><tt> BorderFactory </tt></font><font size="-1" face="Helv,Helvetica,Arial">is more factory than</font><font size="-1"><tt> Box </tt></font><font size="-1" face="Helv,Helvetica,Arial">is, from the theoretical Factory Pattern that is. The difference is that here you '''never''' instantiate an object of that class. From the</font><font size="-1"><tt> Box </tt></font><font size="-1" face="Helv,Helvetica,Arial">class you got</font><font size="-1"><tt> Box</tt></font><font size="-1" face="Helv,Helvetica,Arial">'s, though vertical or horizontal. But the</font><font size="-1"><tt> BorderFactory </tt></font><font size="-1" face="Helv,Helvetica,Arial">never returns a</font><font size="-1"><tt> BorderFactory </tt></font><font size="-1" face="Helv,Helvetica,Arial">(it has no constructors in fact), but you instantiate subclasses to the</font><font size="-1"><tt> Border </tt></font><font size="-1" face="Helv,Helvetica,Arial">super class, as</font><font size="-1"><tt> BevelBorder </tt></font><font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> BorderFactory </tt></font><font size="-1" face="Helv,Helvetica,Arial">will generously provide you with as many you need.</font>
Another example is <tt>BorderFactory</tt>, also a factory, easily read from its name. In fact <tt>BorderFactory</tt> is more factory than <tt>Box</tt> is, from the theoretical Factory Pattern that is. The difference is that here you '''never''' instantiate an object of that class. From the <tt>Box</tt> class you got <tt>Box</tt>'s, though vertical or horizontal. But the <tt>BorderFactory</tt> never returns a <tt>BorderFactory</tt> (it has no constructors in fact), but you instantiate subclasses to the <tt>Border</tt> super class, as <tt>BevelBorder</tt> 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 <tt>BorderFactory</tt> will generously provide you with as many you need.
|}


<font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
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.


<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> Woman </tt></font><font size="-1" face="Helv,Helvetica,Arial">or a</font><font size="-1"><tt> Man </tt></font><font size="-1" face="Helv,Helvetica,Arial">depending on the number:</font>
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 <tt>Woman</tt> or a <tt>Man</tt> depending on the number:
 
public Human getPerson(long ssNumber)
<blockquote><font size="-1"><tt>public Human getPerson(long ssNumber)</tt></font></blockquote>
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.
 
<font size="-1" face="Helv,Helvetica,Arial">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.</font>


===Override a Method===
===Override a Method===
<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> paintComponent</tt></font><font size="-1" face="Helv,Helvetica,Arial">, recall that we as the first line made a call like</font>
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 <tt>paintComponent</tt>, recall that we as the first line made a call like
  super.paintComponent(g);
  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 <tt>paintComponent</tt> with our own method and implementation. If we are not doing that <tt>super.paintComponent(g)</tt> the "grandclass" will never receive any calls and hence can not update the stuff it is responsible for. A veeery common mistake.


<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">with our own method and implementation. If we are not doing that</font><font size="-1"><tt> super.paintComponent(g) </tt></font><font size="-1" face="Helv,Helvetica,Arial">the "grandclass" will never receive any calls and hence can not update the stuff it is responsible for. A veeery common mistake.<br /></font>
But why not simply name our own method differently? Since the system always looks for methods with certain names, in this case <tt>paintComponent</tt>, but there are several other examples. Anyway, if you override a <u>method of the system</u>, make sure you '''always''' do that call to <tt>super</tt>, the ancestor of your class.
 
<font size="-1" face="Helv,Helvetica,Arial">But why not simply name our own method differently? Since the system always looks for methods with certain names, in this case</font><font size="-1"><tt> paintComponent</tt></font><font size="-1" face="Helv,Helvetica,Arial">, but there are several other examples. Anyway, if you override a <u>method of the system</u>, make sure you '''always''' do that call to</font><font size="-1"><tt> super</tt></font><font size="-1" face="Helv,Helvetica,Arial">, the ancestor of your class.<br /></font>
 
<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> raiseSalary(float percentRaise)</tt></font><font size="-1" face="Helv,Helvetica,Arial">. 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</font><br />


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 <tt>raiseSalary(float percentRaise)</tt>. 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) {
  public void raiseSalary(float percentRaise) {
     float temp = getSalary();
     float temp = getSalary();
Line 33: Line 28:
     super.raiseSalary(percentRaise);
     super.raiseSalary(percentRaise);
  }
  }
We overrode the method <tt>raiseSalary</tt>, 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).


<font size="-1" face="Helv,Helvetica,Arial">We overrode the method</font><font size="-1"><tt> raiseSalary</tt></font><font size="-1" face="Helv,Helvetica,Arial">, 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).<br /></font>
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 <tt>raiseSalary</tt> method but we do not do a call to super. Boss simply implements the method as
 
<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> raiseSalary </tt></font><font size="-1" face="Helv,Helvetica,Arial">method but we do not do a call to super. Boss simply implements the method as</font>
  public void raiseSalary(float raise) {
  public void raiseSalary(float raise) {
     float oldSal = getSalary();
     float oldSal = getSalary();
Line 44: Line 38:


===The bugs===
===The bugs===
<font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
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.


<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">method I used the</font><font size="-1"><tt> stopX </tt></font><font size="-1" face="Helv,Helvetica,Arial">and</font><font size="-1"><tt> stopY </tt></font><font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> stopX </tt></font><font size="-1" face="Helv,Helvetica,Arial">and</font><font size="-1"><tt> stopY</tt></font><font size="-1" face="Helv,Helvetica,Arial">.<br /></font>
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 <tt>paintComponent</tt> method I used the <tt>stopX</tt> and <tt>stopY</tt> 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 <tt>stopX</tt> and <tt>stopY</tt>.


<font size="-1" face="Helv,Helvetica,Arial">The solution is to use temporary variables for the width and height, and have them recomputed every time a call is made to the</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">method. And of course never ruin neither the start point or the stop point. Thus we introduce</font><font size="-1"><tt> startXX </tt></font><font size="-1" face="Helv,Helvetica,Arial">and</font><font size="-1"><tt> startYY </tt></font><font size="-1" face="Helv,Helvetica,Arial">as temporary starting values, since we sometimes must switch them with the upper left point as seen below. Further we introduce</font><font size="-1"><tt> height </tt></font><font size="-1" face="Helv,Helvetica,Arial">and</font><font size="-1"><tt> width</tt></font><font size="-1" face="Helv,Helvetica,Arial">. The solution is told in the image below.<br /></font>
The solution is to use temporary variables for the width and height, and have them recomputed every time a call is made to the <tt>paintComponent</tt> method. And of course never ruin neither the start point or the stop point. Thus we introduce <tt>startXX</tt> and <tt>startYY</tt> as temporary starting values, since we sometimes must switch them with the upper left point as seen below. Further we introduce <tt>height</tt> and <tt>width</tt>. The solution is told in the image below.


[[Image:JAVA11A.GIF|thumb|PaintBox class]]
[[Image:JAVA11A.GIF|thumb|PaintBox class]]


<font size="-1" face="Helv,Helvetica,Arial">Is it then possible to outline the ovals while being painted? Yes, of course. Simply declare a new class variable</font><font size="-1"><tt> private boolean mousePressed </tt></font><font size="-1" face="Helv,Helvetica,Arial">among the other declared class variables. Within the</font><font size="-1"><tt> MouseAdapter : mousePressed </tt></font><font size="-1" face="Helv,Helvetica,Arial">method within the class' constructor, add the sole line line</font><font size="-1"><tt> mousePressed = true</tt></font><font size="-1" face="Helv,Helvetica,Arial">. And add its counterpart</font><font size="-1"><tt> mousePressed = false </tt></font><font size="-1" face="Helv,Helvetica,Arial">within the</font><font size="-1"><tt> mouseReleased </tt></font><font size="-1" face="Helv,Helvetica,Arial">method, preferably as the first line of that method. This boolean variable; and the toggling lines, will be used by the</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">method.<br /></font>
Is it then possible to outline the ovals while being painted? Yes, of course. Simply declare a new class variable <tt>private boolean mousePressed</tt> among the other declared class variables. Within the <tt>MouseAdapter:mousePressed</tt> method within the class' constructor, add the sole line line <tt> mousePressed = true</tt>. And add its counterpart <tt> mousePressed = false </tt> within the <tt> mouseReleased </tt> method, preferably as the first line of that method. This boolean variable; and the toggling lines, will be used by the <tt> paintComponent </tt> method.


<font size="-1" face="Helv,Helvetica,Arial">To</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">we will add these lines<br /></font>
To <tt>paintComponent</tt> we will add these lines
  if (mousePressed) {
  if (mousePressed) {
         g.drawRect(startXX, startYY, width, height);
         g.drawRect(startXX, startYY, width, height);
  }
  }
<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> drawRect�... </tt></font><font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
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 <tt> drawRect�... </tt> 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.


<font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
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==
==Keep shapes in a memory==
<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> Vector </tt></font><font size="-1" face="Helv,Helvetica,Arial">found in the</font><font size="-1"><tt> java.util </tt></font><font size="-1" face="Helv,Helvetica,Arial">package.<br /></font>
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 <tt>Vector</tt> found in the <tt>java.util</tt> package.


===PaintShape class===
===PaintShape class===
<font size="-1" face="Helv,Helvetica,Arial">Further we have to make ourselves another class, let us call it</font><font size="-1"><tt> PaintShape</tt></font><font size="-1" face="Helv,Helvetica,Arial">, not to confuse ourselves with the</font><font size="-1"><tt> java.awt.Shape</tt></font><font size="-1" face="Helv,Helvetica,Arial">. 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.<br /></font>
Further we have to make ourselves another class, let us call it <tt>PaintShape</tt>, not to confuse ourselves with the <tt>java.awt.Shape</tt>. 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.


[[Image:JAVA11B.GIF|thumb|PaintBox class]]
[[Image:JAVA11B.GIF|thumb|PaintBox class]]


<font size="-1" face="Helv,Helvetica,Arial">But how will it ever be painted? Please copy the entire</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">from the</font><font size="-1"><tt> PaintPanel </tt></font><font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> if mousePressed </tt></font><font size="-1" face="Helv,Helvetica,Arial">from it.<br /></font>
But how will it ever be painted? Please copy the entire <tt>paintComponent</tt> from the <tt>PaintPanel</tt> 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 <tt>if mousePressed</tt> from it.
 
<font size="-1" face="Helv,Helvetica,Arial">Finally, change the tests to look like </font><br />
 
<font size="-1"><tt>    if (shape == PaintPanel.LINE) { // that is line</tt></font>
 
<br /><font size="-1" face="Helv,Helvetica,Arial">and</font><br />
 
<font size="-1"><tt>    if (shape == PaintPanel.RECTANGLE) { // that is rectangle</tt></font>
 
<br /><font size="-1" face="Helv,Helvetica,Arial">The class is now complete.<br /></font>
 
====<font face="Helv,Helvetica,Arial">'''PaintPanel class<br />'''</font>====
 
<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> Vector </tt></font><font size="-1" face="Helv,Helvetica,Arial">among the declared variables, since</font><font size="-1"><tt> Vector </tt></font><font size="-1" face="Helv,Helvetica,Arial">gives us a good store<br /></font>
 
<font size="-1"><tt>    private Vector storedShapes;</tt></font>
 
<br /><font size="-1" face="Helv,Helvetica,Arial">and the line<br /></font>
 
<font size="-1"><tt>    storedShapes = new Vector();</tt></font>
 
<br /><font size="-1" face="Helv,Helvetica,Arial">within the class' constructor. Since</font><font size="-1"><tt> Vector </tt></font><font size="-1" face="Helv,Helvetica,Arial">is located in the</font><font size="-1"><tt> java.util </tt></font><font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>


<font size="-1" face="Helv,Helvetica,Arial">Then continue within the</font><font size="-1"><tt> MouseAdapter : mouseReleased </tt></font><font size="-1" face="Helv,Helvetica,Arial">method and add some lines<br /></font>
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.


<font size="-1"><tt>   PaintShape tempShape = new PaintShape(filledShape, paintColor, shape, startX, startY);
====PaintPanel class====
    tempShape.setStop(stopX, stopY);
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 <tt> Vector </tt> among the declared variables, since <tt> Vector </tt> gives us a good store
    storedShapes.addElement(tempShape);</tt></font>
    private Vector storedShapes;
and the line
    storedShapes = new Vector();
within the class' constructor. Since <tt>Vector</tt> is located in the <tt>java.util</tt> 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.


<br /><font size="-1" face="Helv,Helvetica,Arial">at the end, only the </font><font size="-1"><tt>repaint() </tt></font><font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> tempShape </tt></font><font size="-1" face="Helv,Helvetica,Arial">into the store (note that from Java 1.2 you may use</font><font size="-1"><tt> add(Object o)</tt></font><font size="-1" face="Helv,Helvetica,Arial">).<br /></font>
Then continue within the <tt>MouseAdapter:mouseReleased</tt> 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 <tt>repaint()</tt> 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 <tt>tempShape</tt> into the store (note that from Java 1.2 you may use <tt>add(Object o)</tt>).


<font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">of the</font><font size="-1"><tt> PaintPanel </tt></font><font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> storedShapes </tt></font><font size="-1" face="Helv,Helvetica,Arial">object. Simply add these lines at the second first line of the</font><font size="-1"><tt> PaintPanel</tt></font><font size="-1" face="Helv,Helvetica,Arial">'s</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">method, only the call to</font><font size="-1"><tt> super </tt></font><font size="-1" face="Helv,Helvetica,Arial">shall come before these lines<br /></font>
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 <tt> paintComponent </tt> of the <tt> PaintPanel </tt> 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 <tt> storedShapes </tt> object. Simply add these lines at the second first line of the <tt> PaintPanel</tt>'s <tt> paintComponent </tt> method, only the call to <tt> super </tt> shall come before these lines
  for(int i = 0; i < storedShapes.size(); i++) {
  for(int i = 0; i < storedShapes.size(); i++) {
         PaintShape tempObj = (PaintShape) storedShapes.elementAt(i);
         PaintShape tempObj = (PaintShape) storedShapes.elementAt(i);
         tempObj.paintComponent(g);
         tempObj.paintComponent(g);
     }
     }
<font size="-1" face="Helv,Helvetica,Arial">[[Image:JAVA11C.GIF|345px|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</font><font size="-1"><tt> i </tt></font><font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
[[Image:JAVA11C.GIF|thumb|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 <tt> i </tt> 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.


<font size="-1" face="Helv,Helvetica,Arial">A vector always stores Java</font><font size="-1"><tt> Objects</tt></font><font size="-1" face="Helv,Helvetica,Arial">, any kind is possible (except for data types since these are no</font><font size="-1"><tt> Objects </tt></font><font size="-1" face="Helv,Helvetica,Arial">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</font><font size="-1"><tt> PaintShape </tt></font><font size="-1" face="Helv,Helvetica,Arial">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.<br /></font>
A vector always stores Java <tt> Objects</tt>, any kind is possible (except for data types since these are no <tt> Objects </tt> 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 <tt> PaintShape </tt> 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.


<font size="-1" face="Helv,Helvetica,Arial">The retrieved object is stored in a temporary local variable. Finally the object's</font><font size="-1"><tt> paintComponent </tt></font><font size="-1" face="Helv,Helvetica,Arial">method is called, using the parameter</font><font size="-1"><tt> g </tt></font><font size="-1" face="Helv,Helvetica,Arial">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.</font>
The retrieved object is stored in a temporary local variable. Finally the object's <tt> paintComponent </tt> method is called, using the parameter <tt>g</tt> 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==
==Summary==
Line 124: Line 107:
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.
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.


[[Category:Languages Articles]]
[[Category:Java Articles]]

Revision as of 17:41, 14 December 2017

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.