Conservatively Radical Java in CS1

Stuart Reges

University of Arizona
Department of Computer Science
Tucson, AZ 85721
(520) 621-4817

reges@cs.arizona.edu

 


Abstract

Java is fast becoming the language of choice in CS1, but we have yet to figure out how to take full advantage of it’s special features.  The conservatives teach the old course in Java syntax.  The radicals restructure the course to include Graphical User Interfaces (GUIs) and concurrency.  I prefer a “conservatively radical” middle ground where I use modern GUI programs to teach the old course concepts.  I write GUI/concurrent code and ask my students to complete the program by supplying a particular class or two.  Thus, they work on interesting problems without having to understand the details of how my code works.  And in the process, they get a practical introduction to the modern programming experience of writing a small piece of a much larger program, allowing me to emphasize abstraction early.

Keywords

CS1, Java, GUI, concurrency.

1        Introduction

Like many other intro course instructors, I have been asked over the past four years to switch from Pascal to a more modern, object-oriented language.  First I was asked to switch to C++ and a year and a half later, I was asked to switch to Java.  With all of that switching, I have found it difficult to become comfortable enough with the new language to discover how best to teach it to beginners.  But I believe I am starting to understand how to do that in Java.

Because of limited time, my first approach was to simply translate all of my Pascal programs and teach the course the same way I did previously.  Some programs translated better than others, but in general, my wonderful Pascal examples and programming problems lost much of their luster when translated.  I was teaching the old course in a new syntax, but I knew that I wasn’t teaching “real” object-oriented programming.  As a result, the benefit of switching to an object-oriented language was mostly lost.


At the same time, it was obvious that other people were attempting much more radical approaches.  One of the earliest Java CS1 books by Deitel & Deitel [3] teaches applets early in the book and many other books have followed suit.  Koffman and Wolz [4] have written a CS1 book that uses GUIs throughout thanks to several special-purpose classes they designed to simplify the GUI interface.  And authors like Lynn Stein [7] and Fintan Culwin [2] have been eloquently arguing for a radical shift in teaching approach.

2        Goals

In listening to the radicals describe their vision for a new CS1, I find myself wishing that my CS1 could satisfy some of those radical goals.  In particular, I would prefer that the programs I ask my students to write:

·          have a GUI front-end

·          occasionally involve some concurrency

·          use many objects and many classes

·          use “real” objects (not procedural substitutes)

·          be dynamic, not static

It can be useful to read the list above more as an indictment of the course you get when you directly translate a Pascal CS1 course into Java syntax.

We teach a console interface to keep things simple even though almost nobody writes serious programs with such an interface.  We similarly avoid concurrency even though it is becoming more and more common in modern software.

Because we want our students to write short programs for most of the course, they often end up a few objects and classes.  So in searching for simple classes that we might include, we take functions and turn them into objects.  Instead of calling the function with parameters, we construct an object with values and then ask the object to perform a computation on those values.  While this is closer to object-oriented programming, it isn’t a good example of an object with meaningful state that changes over time.

And then there is the dreaded keyword “static.”  You can’t have an application in Java without having a method called main that is declared to be static.  And because main has to be static, any other methods called directly by main and any variables or constants referred to by main also have to be static.  So our introductory students are bombarded with static this and static that when in fact most Java programs have few static elements.

I can recite this list of flaws easily because when I first taught Java, my course had all of them.  But I didn’t see a good alternative because I also felt that I must satisfy the following conservative goals:

·          students learn the same “nuts and bolts” programming CS1 has always taught (arithmetic, conditionals, loops, parameters, arrays, etc)

·          students don’t have to learn GUIs, concurrency

·          students still write small, directed pieces of code to reinforce specific programming skills

·          programs use “standard” Java

The old CS1 course was crowded already.  There wasn’t room to fit in extra topics like GUIs and concurrency without throwing out something else.  Anyone who claims he can is either speculating, lying or teaching an elite group of gifted students.

I expect that many others will have the same conservative goals that I do.  Most of us do not have the luxury to change the content of the introductory course because it has ramifications for the entire curriculum.  And when you teach mere mortals, it takes time to explain topics like GUIs and concurrency, time that we don’t have.

We have discovered in teaching CS1 in Pascal that beginning students can manage about 50 to 100 lines of code in a weekly programming assignment.  The move to Java does not increase that limit.

And finally, there is extreme resistance to anything that is not “real” Java.  If we ask our students to use our simpler IO package or our custom applet, they believe that we are somehow cheating them.

3        Blasting the Conservatives

Before discussing the “conservatively radical” programs I’ve been using recently I want to point out what I think is wrong with the conservative approach.  As I mentioned earlier, I am able to effectively criticize these approaches because I’ve been guilty of using them myself.

My understanding of these problems was greatly enhanced by attending Michael Kölling’s seminar at the 1999 SIGCSE Technical Symposium [5].  He was arguing that we should no longer teach “hello world” as the first programming assignment.  That might have been a good choice for the procedural languages, but not for languages like Java.  He argued that we should be teaching objects from day one.  He manages to do so by using a sophisticated programming environment (BlueJ) that makes it easy to produce visual representations of objects.

3.1             Static versus dynamic

Even before I heard Michael speak I had found myself bothered by the fact that so many of my programs involved static methods.  I’ve seen the same pattern in most of the introductory Java textbooks.  Any Java application must have a method called main and it must be declared to be static.  That, in turn, means that any method called by main must also be static.  So we either write programs that are just a few lines long, or we write programs with all of the code in method main, or we write programs that are broken down into several static methods.  None of these solutions is acceptable.

The answer is to introduce objects early, but almost nobody does so.  Even when objects are introduced early, they tend to be terrible examples of objects.  Our paradigm in procedural programming was input/process/output  Now that we’re programming in Java, we are creating objects that encapsulate that paradigm.

3.2             Procedural objects

I looked at five of the best selling CS1 Java textbooks to find the first example of a class with non-static fields that wasn’t an applet (I’ll address applets next).  I was disappointed to find that many of these books weren’t discussing classes until the middle of the book.  But even more distressing was that fact that the first examples were all slight variations of input/process/output.  In other words, we are showing students procedural examples thinly disguised as objects.

One of these textbooks had a class with shared data fields that had exactly three methods: one for input, one for processing and one for output.  A second had the same form, but the process/output methods could be called multiple times after a single call on input.  Two others were of the form construct/output where values were passed to a constructor rather than having a reading method.  The fifth had a constructor and a toString method, which is just another variant of construct/output.

While I’m condemning the terrible examples in the CS1 Java books, I should point out that most of the C++ books suffer from the same problem.  The one exception I found was Owen Astrachan’s CS1 textbook [1].  His first class examples are a balloon object and a dice object, neither of which is a procedural object.

Many instructors and textbooks use applets as a way to avoid the static trap.  While I think this is better, it isn’t a lot better.  Most people use applets for drawing pictures.  So we basically are in the same old paradigm, but now the output is graphical.  The programs typically involve just a draw method or a combination construct/draw or init/draw.

Lynn Stein [7] has argued that we must move to a paradigm of interaction rather that computation in CS1.  I’m not prepared to go that far, but I do think that we need to show our students examples of objects that involve nontrivial interaction.  That means we need objects with state that changes over time as the object is manipulated.

In a modern program an object won’t know in advance the order in which its methods will be called.  For example, with an input/process/output object, what if you call input followed by output without calling process in between?  What if you call output immediately without calling either of the other two?  Many of the examples in these intro Java books would produce spurious output in those cases.  This is very bad style to show to novice programmers.

3.3             Modal GUIs

I’d also like to argue against a seemingly radical approach that I think is fundamentally conservative.  When using applets, you can fairly easily bring up a dialog box using the JOptionPane class.  Others use custom GUI classes that have simple methods for bringing up an input dialog box.  This is certainly more modern that the old prompt and read in the console window, but from the point of view of interaction, it is just the old input command dressed up to look more pretty.

These are what are known as “modal” dialog boxes.  Larry Tessler, one of the pioneers of object-oriented programming, has popularized the phrase “don’t mode me in.”  Users don’t like to be trapped into a mode where they lose control.  Consider, for example, a program with seven text fields for entering values and a button that says “Go.”  When I run that program, I get to decide what values to enter and in what order.  Plus I have the option of changing one or more and running it again.

The modal version of this program is just about as annoying to use as the old console programs.  Whenever the program is executed, seven modal dialog boxes appear in succession, each one refusing to go away until I supply a value, and then the program executes once.  I can’t back up and change a value I gave a minute ago.  And if I want to run the program again with a slight variation, I have to execute again and enter all of the values over again.

4        Sample Programs

Finding good sample programs has been the most difficult obstacle I have faced in making the transition from procedural to object-oriented programming.  After teaching Pascal for 10 years, I had a good collection of programs to use as examples in lecture and to assign to students as homework.  Those programs have not, in general, translated well into an object-oriented language.

I’ve been searching for good examples of classes to use in CS1.  Ideally I want classes that are relatively simple to understand but which are also not trivial or silly classes.  I considered, for example, creating a counter object with methods like reset, increment and getCount, but this isn’t a compelling example.  How does the existence of such objects make programming easier than if you just use simple int values?

I believe I am not alone in this search.  As pointed out in the previous section, many of the best-selling CS1 Java textbooks use examples of classes that are poor examples of object-oriented programming.  The problem is that we need to find a new set of examples that have interesting but relatively simple classes.

At one point in time I was having so much trouble coming up with examples that I almost abandoned my plans to change my course.  Then I thought of the adding machine example that I describe below and somehow I have managed to continue to come up with other examples.

4.1             Adding Machine

I wrote a Java application that simulates the behavior of an adding machine (see Figure 1).  I limited it to just integers to keep the computations relatively simple.  The adding machine has buttons for entering digits, buttons to add or subtract the current number and a reset button to reset the sum to 0.  The outer frame has controls for resizing and exiting.

This was the first program example I showed to my students.  We didn’t look at hello world or any other classes with static method main.  I began by executing this program and discussing it with them.  I had previously given a lecture in which I discussed concepts like object, state and behavior.

When I asked students to identify the objects in this application, they quickly came up with the fact that the buttons are all objects and that the frame itself is an object.  Someone then pointed out that the display area is another object.

Figure 1: Adding Machine

Then someone cleverly pointed out that “someone needs to do the math.”  Yes, I said, we want an object to do the math involved.  I explained that it is a rule of thumb in object-oriented programming that the user interface code should be separated from the domain specific code.  In this case, we have a frame with buttons and a text field as controls, but we could easily imagine a different interface.  For example, we might be asked to program the logic for an actual physical adding machine.  The underlying logic is the same no matter what interface is used, so it is helpful to separate it into its own class.

So I asked my students what state and behavior is associated with the adding machine.  At first they just seemed to think that it kept track of “the number.”  But as I quizzed them about the behavior, they were able to see that there was more than one number involved.  As the user enters individual digits, a current number is built up.  Then when the user hits the plus or minus key, the current number is added to or subtracted from the current sum.  And the clear button would reset everything to 0.

So we had identified at least two pieces of state—current number and current sum—and four methods: add a digit, plus, minus and clear.  But we had missed an important method.  How does the user interface find out what number to display?  We needed a fifth method for getting the current display value.

I called the class Accumulator and had prepared in advance a version with stubs for all of the methods.  So I had to spend some time discussing the general form of a class definition and the methods, but I told them not to worry too much about syntax just yet.  I then ran this version of the adding machine and they could see that it executed, but it always displayed the value 0.

So then we started adding variables to keep track of the state and adding assignment statements for updating the state.  The students didn’t immediately realize how to add a new digit to the current number, but then someone suggested multiplying the old one by 10 and adding the new digit.  And we added code for the plus and minus methods to update a cumulative sum.  And we had the get display method return the current number.

When we ran that version of the program, we found that it displayed numbers other than 0, but it still had two significant problems.  First, the adding machine wasn’t displaying the cumulative sum when the user hit the plus or minus key.  It might have been keeping track of it correctly, but we never saw it.  As we pursued this bug, the students came to realize that we needed a third piece of state information.  We needed to know what to be displaying right now.  As the user hits digit keys, the adding machine displays the current number.  But as soon as the user hits the plus or minus key, it should instead display the cumulative sum.

We solved this problem by adding a third integer variable that stored the value to display.  We edited the various methods to assign the appropriate value to this variable and we used it as the value to return from the get display method.

But we still had a bug.  Now we could add together several numbers, but we were always manipulating the same expanding number.  If we typed in 3, 4, 5 and plus we saw 345 in the display.  Then if we typed 2, 5, instead of seeing 25, we saw 34525.  The old digits 345 were still there.  My students quickly realized that whenever the plus or minus methods are executed, we have to reset the current number to 0 so that future calls on add digit start from scratch.

The fact that it took us fifteen or twenty minutes of lecture time to work out these details indicated to me that the class was not trivial or silly.  At the same time, I believe that most of my students understood what we were doing in terms of accounting for digits, resetting values and keeping track of what to display.  So this is a prime example of the kind of class I’ve been searching for.

I presented this class in my third lecture.  The first lecture was for administrative details, the second was an overview of object-oriented programming and this was the first lecture that involved discussion of Java code.  I told my students not to worry about all of the syntax.  I spent the fourth and fifth lectures going over this class in more detail and discussing the syntax details of assignments statements, data fields and methods.

Obviously this program involved more than just the Accumulator class, but I had explained to my students my plans.  They understood that there was supporting code for the user interface.

4.2             Lunar Lander

For the first programming assignment, I asked students to write a class for keeping track of a lunar landing simulation (see Figure 2).  This is a greatly simplified version of the classic lunar landing game.  The object is to apply thrust so as to land at a safe velocity.  Fuel is limited, so if the user applies thrust too early, there won’t be enough fuel to slow down later.  The game can be won fairly easily once the user gets used to allowing the lander to speed up at first and get closer to the moon’s surface and then quickly applying thrust to land it safely.

Some of the state and behavior are obvious for this object.  It needs to keep track of the current fuel, current velocity and current altitude.  These values need to be displayed by the user interface, so we need “get” methods to ask for their values.  Because the simulation can be reset at any time, we also need a method to reset the simulator.

But how and when do we update these values and how is thrust applied?  When I demonstrated this program in class and asked about the objects, my students were able to come up with most of the objects involved.  But there was one object they hadn’t mentioned, so I let them puzzle over it.  Finally a student said there must be some kind of counter or something that kept track of the simulator’s progress.

I told them that I had implemented this by adding a timer object that would ask their object to update its state once every second.  So I introduced a method called “tick” that the timer called once a second.  The tick method had to update the fuel, velocity and altitude for one more second of flight.

To deal with thrust requests, I introduced a method called “thrust” and told students that it would be called once every time the user hit the thrust button.  Their tick method was supposed to apply the number of thrust units requested since the last tick.


Figure 2: Lunar Lander

As in the adding machine example, I wrote all of the user interface code and told them that they had to focus only on the class for keeping track of the lunar lander state.  I gave them a folder that had all the code other than the lunar lander class.

As noted earlier, many of the state variables for this class are fairly obvious.  But there was at least one that wasn’t immediately obvious.  I told them how the thrust and tick methods should behave, but I didn’t tell them how to get that behavior.  I left it to them to realize that they wanted an extra state variable to keep track of the number of thrust requests that had been made since the last tick.  This counter had to be reset to 0 on every call to tick, although I didn’t tell them that in the assignment handout.  I specified the object’s behavior and left it up to them to figure out how to achieve this behavior.

5        Conclusion

This experiment is still in process, so I don’t know yet what my final conclusions will be.  But so far I am pleased with the results.  I don’t think this approach would pay off as much if I didn’t have projection capability in lecture.  Without the ability to show these GUI programs and iterate through several versions of a class, this approach would be less successful.

Students seem to enjoy the programs more and our class discussions have been more fun.  Everyone seems to wake up when I switch to the computer and execute a program.  And most importantly, the students seem to have a better concept of what an object is.  In previous semesters I had students struggling half way through the course with concepts like data fields, methods and objects.  That hasn’t been a problem this semester.  Of course, now my students might find it difficult to understand console or file based programs (input/process/output), but I’m not terribly concerned about that.

I should mention that this approach is not without its pitfalls.  Let me mention just a few.  First, it has worked only because I have been willing to spend many hours developing each sample program.  I can fairly quickly develop a short console program, but a good GUI program takes a lot of time.

Probably the second biggest problem has been confusion over how their code fits with mine.  When they wrote all of the code themselves, they didn’t have to struggle with this.  Now that I am writing code that relies on their code, it is essential that the pieces fit together properly.

For example, with the lunar lander assignment I told them the names of all of the methods they were to implement, but I did not specify the return types.  I wanted them to have to figure out which ones were void methods and which ones returned values.  Unfortunately, some students turned what should have been void methods into methods that returned ints.  When they tried to execute their code with mine, they got odd results.

As another example, one student took a header that I had written and added extra parameters, not realizing that this wasn’t allowed.  Then when the student compiled the code, error messages appeared for my code because the method my code was expecting wasn’t implemented.

Every teaching style has its own benefits and problems, so I expect this one to be no different.  It will take me time to discover how best to avoid the problems and I’ll never be able to avoid them all, but the benefits seem to outweigh the problems.

In 1997 Rich Pattis presented a paper at the SIGCSE Symposium on an “artificial life framework” that he calls “Get A Life” [6].  Get A Life is a more elegant version of the kind of programming problems I have been giving.  Rich provides a complete system of classes that students extend through inheritance to have new behavior.  This avoids many of the pitfalls of counting on students to write a vital component of the system.  If Get A Life becomes available in Java, I will consider using it as the first program that I discuss and the first one or two programming assignments that I give.

As one final note, I wanted to mention that while I think this has been a successful approach for classroom teaching, I am not convinced that it would work well in a textbook.  The volume of supporting code is so great that these examples might not work well in a book.  And if a book were to focus on a small number of such examples, the examples might become tedious.  The lunar lander was fun for a week, but if it became the primary sample program for an entire course, it would be hated by the end of the semester.

More information and full versions of these and other programming problems can be obtained at the following web site:

http://www.cs.arizona.edu/people/reges/sigcse

References

[1]     Astrachan, O., A Computer Science Tapestry, McGraw Hill, 1997, 1999.

[2]     Culwin, F., Object Imperatives!, SIGCSE Technical Symposium, 1999

[3]     Deitel & Deitel, Java, How to Program, Prentice Hall, 1995, 1997, 1999.

[4]     Koffman, E., and Wolz, U., Problem Solving with Java, Addison Wesley, 1999.

[5]     Kölling, M., Tools and Techniques for Teaching Objects First in a Java Course, SIGCSE Technical Symposium, 1999

[6]     Pattis, R., Teaching OOP in C++ Using an Artificial Life Framework, SIGCSE Technical Symposium, 1997

[7]     Stein, L., Beyond Objects, Educator’s Symposium, Conference on Object Oriented Programming Systems, Languages and Applications, 1997