© 2000-2003 A-Square, Inc. Cambridge, MA
Banner2 in its several variations is considered in Exercise 2 of the Introduction to Java Tutorial
Comments to Banner2a.java
General comparison with Banner1
private void manageScreen()
public boolean mouseDown
public void update(Graphics g)
private void paintBanner
Comments to Banner2b.java
Differences with Banner2a
Comments to Logo
public abstract class Logo extends Component
public Logo(int xm, int ym, int size)
public void moveLogo()
stopMove(), resumeMove() and setMaxSpeed
Utility Methods
Comments to ASquareLogo
Comments to IEEElogo
In general you will find the code of Banner2a and Banner2b similar to the one of Banner1 in Exercise1. However, some differences show how things are simplified when we apply Object Oriented thinking.
import is the same which is understandable since Banner2 and Banner1 do the same thing. They are both applets and they do graphics for which they need the awt library.
public class Banner2a extends Applet implements Runnable is identical. In particular we still wish to make the applet Runnable so we have to implement the run() method.
init() looks different but is similar. We are making the applet a little more sophisticated by allowing it to adapt to a changing window. This can not happen in a browser, but it can happen in an AppletViewer and the same technique is used to adapt to a changing window for an application where the changing the size of windows should be expected.
This is a method where we have assembled all that needs to be done when the screen changes size which not coincidentally is the same which needs to be done when we start up the applet the first time. That is, we get ourselves a double buffer and we initiate a logo. However, we now use a class ASquareLogo which is derived from the abstract class Logo. We have a variable logo which is declared private ASquareLogo logo;
When we have determined the size of the screen (mx1, my1), and the preferred size, size of the logo, we can get ourselves an ASquareLogo object as follows
logo = new ASquareLogo(mx1, my1, size);
This is one of the keys to Object Oriented programming. The ASquareLogo describes a class of objects, say ASquareLogo in general, and when you need one you simply say new and follow it with a call to the ASquareLogo constructor. A constructor in Java initiates a new object using parameters that tailor the object for use in some particular context. In our case we give the maximum x and y coordinates as parameters. We also give the size (initiated to 35 pixels, which was what we used in Banner1). We say that ASquareLogo has been instantiated and the value given to the variable logo. And we can now access any public methods defined in ASquareLogo through its representation in logo. Right away we would like to give our new logo a maximum speed as it bounces around the screen. We do this using a public method designed for the purpose:
logo.setMaxSpeed(maxv);
We will speak more about setMaxSpeed when we discuss the abstract Logo class. Once you have understood this part you will have come a long way to understanding the principles of Object Oriented programming.
start(), run() and stop() are identical to the same methods for Banner1
public boolean mouseDown(java.awt.Event evt, int xd, int yd)
The mouseDown method does the same thing now as it did for Banner1. However, we can not directly deal with dx and dy the speed components of the bouncing center of the logo. These variables are private and inside ASquareLogo as far as we know. Instead we have to use two special methods that we have implemented stopMove() and resumeMove(). Not coincidentally you can also see that the logic of this method becomes much clearer with this modification.
public void update(Graphics g)
This method is again almost identical to the update method in Banner1. We have only added code to detect any change to the window size and take appropriate action if the size of the window has indeed changed. As we observed under init(), the manageScreen() method takes care of any changes.
This gives us the opportunity to speak about garbage collection. If the window size changes, the logo variable is given a new value, the value of a new instance of ASquareLogo adapted to the new window size. You might wonder what happened to the old logo. The answer is that all that now irrelevant and useless data that constituted the particulars of the old logo lies around as garbage until it is garbage collected. The space is linked into a list of available memory space and can thus be recycled. Other languages, say C or C++ requires a programmer to track used memory resources and manually indicate when a resourse can be recycled. This error prone activity has to be done manually because only the programmer knows what references to the object can still occur. One of the great innovations in Java is the elimination of references as an object type that can be manipulated. In Java a low level program goes around and checks all existing references. If an object has no references to it, it can be garbage collected. In our case we can see that the only reference to the particular instance of ASquareInc was in the logo variable and so when we give it a new value in manageScreen(), we know that any memory used by the old instance safely can be recycled and that at some point the garbage collector will come around an do that.
private void paintBanner(Graphics g)
paintBanner again has the logic of the same method of Banner1. However, a long sequence related to the movement and bouncing of the logo has disappeared. In the case of Banner2, we have moved the sequence so that we in this paintBanner instead can write
logo.drawLogo(g);
logo.moveLogo();
Here we have another example of the benefits of Object Oriented programming. While the logo.drawLogo(g); replaces the drawAsquare, the greatest gain is when we now, again with a clear gain in logic visibility, can replace the complicated sequence related to moving the logo around with a simple invocation of logo.moveLogo().
Standard Methods
We have retained the printString method from Banner1, but the other methods are now moved to the Logo class.
public abstract class Logo extends Component
Logo is an abstract class. It has to be called abstract because it has two methods that are declared abstract. An abstract method is one which is defined only with regard to its signature. It is anticipated that any extending class will provide the particulars of implementation. That Logo is abstract also means that it can not be instantiated by itself. However, any class that extends Logo and implements the abstract methods can be instantiated. Below we will see how ASquareLogo does its thing when it implements the abstract methods in a way that is unique for itself.
According to the declaration, Logo also extends component. This has no relevance in the case of ASquareLogo of Banner2a but provides some properties that are necessary for the IEEElogo of Banner2b.
This is an example of a constructor. It has the name of the class as its name and can have parameters as indicated here. We said before that an abstract class can not be instantiated. However, classes derived from an abstract class can be instantiated and we shall see how the constructor of ASquareLogo simply passes on the parameters to its parent for instantiation.
One fact may be noted. The variable maxv determines the maximum speed of the logo and there is a method setMaxSpeed(int v) that sets maxv as we shall see. However, we also decided that we would have a kind of default maxv derived from the physical dimensions of the window:
maxv = 2+(maxx+maxy)/100;
For the window initiated in banner2.html, this would give a maxv of about 7.
The Logo class has two abstract methods. One draws the logo and is obviously very specific to the logo in question. One method that I thought might be useful when dealing with logos, was one that erases the logo by drawing over it with a background color. And so we have the following two methods related to how the logo is drawn
public abstract void drawLogo(Graphics g);
public abstract void eraseLogo(Graphics g, Color c);
As it happens, Banner2a or Banner2b do not use the erase method. I thought it would be nice and symmetric but perhaps I was wrong and perhaps an eraseLogo is totally unnecessary. If it is, I have given some extra work to anybody implementing a class derived from Logo and I should apologize in advance. Let me take the opportunity to describe how an alternative solution could have been designed.
Suppose we have a class designed exactly like Logo above except that it does not prescribe the eraseLogo method. Let us name this class UnEraseableLogo. We can now declare a new class which in its entirety is described as follows:
public abstract class ErasableLogo extends UnEraseableLogo{
public abstract void eraseLogo(graphics g, Color c);
}
This is another advantage of Object Orientation, where you loose little in deriving new classes with new or slightly different functionality. As long as the user understands the functionality which, of course, is defined by the public methods and variables of a class, the user can do its own thing by subclassing. By subclassing UnEraseableLogo, EraseableLogo has the exactly the functionality of UnEraseableLogo plus the functionality that is added by the abstract method eraseLogo. So, in fact, EraseableLogo has the functionality of the Logo we started with. Except that I probably should given the UnEraseableLogo a less convoluted name, we have given the users of the class a clear choice of implementing or not implementing the eraseLogo method by deriving their class from EraseableLogo or UnEraseableLogo. respectively.
On the other hand, this example also is an indication of the need for intelligent systems thinking. Experience and many false starts may be required before a class hierarchy has its optimum shape that should encourage reuse exactly where changes in requirements are going to happen. In this regard, the source of the Java libraries give a number of excellent case studies showing how the class hierarchies can be intelligently designed and implemented to encourage efficient reuse, some of which has happened already in the libraries themselves, but some of which are left for us programmers to do as we solve the problems of the real world.
We now have collected everything about the moving of a bouncing point in the moveLogo method. It is functionally the same as that section of code that described the bouncing of a logo in the paintBanner method of Banner1. Now it is all together, however. And the code is easier to read as well as to modify because we know exactly what it is supposed to do. Now any logo that is derived from the Logo class will move exactly as we have described here.
However, if a subclass for any reason wishes to move in another way it can do so by implementing its own moveLogo() method. It must be called exactly moveLogo() and have the same signature as the moveLogo of the Logo class. We say in this case that we override the method of the parent class. Overriding is a common technique in Object Oriented programming.
We shall now for a moment dwell on a particular Java syntax. Consider the following statement:
dx = (x < 0)? ABS(dx) : - ABS(dx);
This may look at first as a confusing notation with a conditional branch inside the expression. It is actually a concise and efficient notation for what could have been expressed as follows:
if (x<0) // If the center of the logo has moved to the left of the window
dx = ABS(dx); //Then make sure dx is positive, so the logo will be moving toward the right
else // If the center has moved to the right of the window (we know from the very
// first conditional (x > xmax) that it it is outside the window)
dx = - ABS(dx); // Make sure dx is negative, so the logo will be moving toward the left
That is, the statement makes sure the point always moves toward the window if it is outside. This has the effect that the point sooner or later will end up inside the window which may be good to know. Gives us the option of not generating a new Logo if the windows size changes. Think about it!
Now that we have the moving of the logo all defined inside the Logo class we need some way to stop the logo from
outside and to have it resume moving. This is the function of the two methods
public void stopMove() and public void resumeMove() respectively. This is another case where the advantage of Object Oriented programming is apparent. The methods are very simple as it turns out but they are well named. Only their signatures are known to the outside world. Suppose now we would like to make a change because we realize that if the method resumeMove is called before stopMove, we may have a situation when the dx1 and dy1 are 0 so the Logo never moves. One way to avoid this is to initiate dx1 and dy1 with a non zero value, for example as follows; int dx1 = 2; and correspondingly for dy1. However, it may be more obvious if this is done in the resumeMove as follows (using again the compact notation discussed just above:
instead of dx=dx1; we write dx = (dx1 > 0)? dx1 : setSpeed(vmax);
I used setSpeed here rather than just a constant because I just think we should have randomness consistently and setSpeed will always give a non zero value. - - -
Or will it? What about if someone from the outside sets vmax to 0 by invoking setMaxSpeed(0)? Wow! setSpeed will go into an infinite loop. We just discovered a bug and had better ensure that this never happens by prescribing that vmax is at least 2. We can change the setMAxSpeed so that it sets maxv to minimum 2 as follows:
public void setMaxSpeed( int vmax){
maxv = (ABS(vmax)<2)? 2 : ABS(vmax);
}
By using the absolute value of vmax we are also taking care of the case if a strange user of the class should try to give a negative value to vmax. You should now document the change to setMaxSpeed stating that the minimum value is set to 2 if someone tries to set it smaller. Now, suppose someone wishes to give the logo a 0 speed. Well then, let him or her use the stopMove and resumeMove pair, of course!
We still wish to modify resumeMove and the new code will look as follows:
public void resumeMove(){
dx = (dx1 != 0)? dx1: setSpeed(maxv);
dy = (dy1 != 0)? dy1: setSpeed(maxv);
}
Since we are concentrating the functionality of a bouncing logo in the Logo class we may see from our early discovery of a potential bug that it is worthwhile to think through the way the class can be used and prevent any odd exceptions from happening by filtering out bad inputs in the setters. This ensures that the class is indeed reusable and you will get a gold star as its creator.
protected Image getRartImage(String s)
In the IEEELogo class we would like to import the IEEE logo which actually was given to us as a gif-file. We have here provided a utility method that seems to work. However, it does require that we have access to some methods that are provided by the Java Component class. That is the reason why we defined Logo as extending Component. We have here used the getRartImage method defined as part of the Rart support software. Exercise 3, will give more details about Rart.
RND(int x), and ABS(int x)
The Logo constructor and the moveLogo method both use the utility methods RND and ABS. If we place them in Logo, they can of course be used in Logo itself. In addition they can be used in any class that extends the Logo class.
A word about protected. Methods and variables declared in a class have been called private or public with obvious meanings: public entities can be referred to outside the class but private entities cannot be referred to outside the class. The words private and public are called qualifiers. There are more of them. Of interest now is the protected qualifier. If a variable or a method is declared protected, the variable or method can only be referred to outside the declaring class in classes that extend the declaring class.
So now you know.
It is very satisfying to provide a complete listing of the differences between Banner2b and Banner2a since there are so few differences. In fact, we are looking at changing two lines of code (replacing ASquareLogo with IEEELogo) and doing a touchup of the paintBanner method:
In manageScreen(), one line initiating IEEELogo rather than ASquareLogo:
logo = new IEEELogo(mx1, my1, size);
Declaring the variable Logo:
private IEEELogo logo;
Strictly speaking we do not need to modify paintBanner at all. However, we thought that the banner would look better if we changed the background color to white, since the blue IEEE logo was provided to us as a gif-file on a white square background. This background now becomes invisible against the white background of the Banner2b applet. For the same reason it makes sense to draw the strings after, i.e. over the floating logo. Of course, if we wanted to we could now go back make the same changes to Banner2a. However, this kind of tweaking is what you have to do all the time and we left it in the tutorial as a realistic example of how to reuse another class with a minimum of modifications..
To complete this listing of differences we list here the new paintBanner in its entirety:
private void paintBanner(Graphics g){
// This method paints the whole rectangle (0,0, maxx, maxy)
g.setColor(Color.white); // Looks better with IEEE logo
g.fillRect(0, 0, maxx, maxy); // Paint the screen white
//For IEEE it looks better to have the Logo under the strings
logo.drawLogo(g); // Draw the logo
logo.moveLogo(); // Move the logo
printString(g, s1, maxx,45 ,24);// Draw the three strings
printString(g, s2, maxx,70 ,12);
printString(g, s3,maxx,maxy-5,9);
}
When we do the ASquareLogo, we know that we shall implement the two abstract methods drawLogo and eraseLogo. We also need a constructor. Do we need anything else? No! Everything else we need is already defined in the Logo class. And it is defined once and for all with special efforts to anticipate uses.
Consider the constructor:
public ASquareLogo(int xmax, int ymax, int size){
super(xmax,ymax,size);
}
We are simply calling the constructor of the parent class. This way is so common that Java has defined a name, super, that can be used to referred to a parent class.
public void drawLogo(Graphics g)
The code is exactly the one from Banner1
public void eraseLogo(Graphics g, Color c)
The code is simple and can be derived from the code of drawLogo, since drawLogo starts by drawing a rectangle and then everything else inside that rectangle.
There are amazingly few comments to be made about IEEELogo. The only ones which make it different from A-Square have to do with the fact that drawLogo in ASquareLogo actually draws the logo from scratch using primitive AWT methods, in particular fillRect( . . .).
For IEEE we were given the IEEE logo as a gif-file and the method of importing and scaling it is general enough that it is worth implementing and describing. Of course, importing a file in an applet is not allowed in a browser for security reasons and so this applet can not be immediately downloaded and executed over the Internet. However the methodology would be similar in an application which can be given more permissions and the applet as is can be viewed in the AppletViewer that is part of JDK.
public IEEELogo
The constructor starts out like the ASquareLogo constructor by invoking the constructor of the parent, that is Logo. But then we use the constructor to import the logo from the gif-file:
Image logo1 = getRartImage("num3blu.gif");
The method getRartImage is actually defined in the Logo class where it is protected which means it is available here since IEEELogo extends Logo. We don't even know at this point how big the logo is that we import and so the next step is to use an Image handling facility to scale it:
logo = logo1.getScaledInstance(2*size, 2*size, Image.SCALE_FAST);
Note: The variable logo is here declared as private Image logo;, which is not to be confused with the variable logo used at some length in both variations of the Banner2 class whare it is declared as ASquareLogo logo; and IEEELogo logo; respectively!
Finally we have to modify the drawLogo method to make it, in fact, draw the IEEE logo.
Again we will reproduce it here in its minimal entirety:
public void drawLogo(Graphics g){
int w=s; //half width
int h=s; //half height
g.drawImage(logo, p.x-w, p.y-h, this);
}
Actually the use of w and h in this context is entirely uneccesary. I had it here only to provide some kind of point of reference with ASquareLogo. The method could just as well have been written as:
public void drawLogo(Graphics g)
g.drawImage(logo, p.x-s, p.y-s, this);
The braces that often follow a declaration of a method are there to keep mark the beginning and end of the block of statements that form the body of the method. In this case the method has just one statement so we can skip the braces for this time. However, they would do no harm and perhaps consistency is preferrable to brevity. Just wanted you to know about it!
Another oddity in the drawImage statement is the this used as a fourth argument. this when used like this (keep the this in default font separate from the this in courier font, please) indicates the present instance of the class where the this appears. In this case the this indicates that the current instance of the Logo acts as imageObserver. This instance of the IEEELogo class can act as an imageObserver because it is an instance of a class that extends Logo that extends Component and Component implements the interface ImageObserver. This may seem complicated but the fact is that you need to dive into these complications only when you are trying to do something special. While it may take some time, in the end you know that you have a good solution and you will learn a lot by following the links of the Java documentation .
That is it! Done with Banner2! Let's have a cup of coffee!