Opinion
Computing Applications Forum

Forum

'Hello, World' Gets Mixed Greetings
Posted
  1. Introduction
  2. The Emperor's Wardrobe
  3. Targeting Zeppelins
  4. Author

Ralph Westfall is absolutely right in "’Hello, World’ Considered Harmful" (Oct. 2001, p. 129). The classic "Hello, World" program is a miserable introductory example to OOP. Unfortunately, Westfall does not go far enough in his analysis, and his cure is nearly as bad as the disease he identifies.

Westfall gives two criteria for early OO examples: they should instantiate objects and use object behaviors. A more complete list of criteria would include:

  • Objects should be explicitly instantiated (excluding strings and System.out). Understanding how objects are created is a vital part of learning to think with objects.
  • Objects should have their methods invoked, otherwise students view them simply as data containers or abstract pieces of syntax.
  • Each object should have easily discernable state and behavior. If not, the two core aspects of an object will remain a mystery to students.
  • Examples should contain two or more instances of the same class to drive home that each object has its own state but shares behavior with other members of its class.
  • Static methods, other than main, should be avoided because they don’t affect the state or behavior of individual objects, thus clouding two core concepts.
  • Students should not need to master both using objects and writing classes at the same time.

Westfall’s solution meets the first two of these criteria, but fails on the rest.

A better solution is for students to learn to use objects first. After understanding what objects are and how to use them, students can proceed to write their own classes. Besides being a better way to learn OOP, using a library of predefined classes to direct turtles to draw, or bring artificial cells to life, or to manipulate robots in an imaginary world is much more interesting than printing "Hello, World."

Byron Weber Becker
Waterloo, Ontario

Ralph Westfall’s alternative to the "Hello, World" program is not a true OOP example either. The solution he presents defines a stateless class with a static method, printHello(). Because it’s stateless, there would be no need to differentiate between instances. Furthermore, since the printHello() is static, it’s a class method not an instance method. As a result, we have a thinly veiled procedural structure.

An object’s characteristics include properties, functionality, and state. A problem is decomposed into a network of objects that collaborate to solve the problem. We can fiddle a bit with the program to provide a proper illustration of OOP by defining a class that has a state; it makes instantation meaningful. (I include here simple Greeter class greetings objects as an example.)

But this is such a contrived problem that to make it illustrative I would include as state of the object, a table of different greetings, and possibly a table of cookies to be given randomly every time the program is run, including an instance of this class. Adding state makes it smarter, and provides the chance to illustrate how objects collaborate in the solution to the problem (need Random generator and a List class)

  • import java.util.Date;
  • public class Greeter{
  • public Greeter (String greetings){
  • greet = greetings
  • date = new Date ();
  • }
  • public String greetings (){
    • return greet;
  • }
  • public String date (){
    • return date.toString ();
  • }
  • private String greet;
  • private Date date;
  • This is why "Hello, World" is considered harmful: In true OOP, objects are used to decompose the problem, and their interaction provides the solution desired. Each object used has a state; the user interface is a completely separate component of the system. Therefore, the simpleton "Hello, World" program really has no pedagogical value to illustrate the use of objects.

    This opinion also goes for teaching programming via complete one-page, throw-away programs of which the "Hello, World" is an example. These programs usually serve as a stage for syntax gymnastics, but are barren of illustration on best practices for system decomposition and coding. Moreover, they create an opportunity for students to acquire bad programming habits. As programming teachers, we should strive to provide the best examples possible in decomposition and coding, while making sure the artifacts we use exemplify typical occurrences.

    Jaime Nino
    New Orleans, LA

    Ralph Westfall suggests clarifying the use of objects when teaching introductory Java by having main() explicitly instantiate a separate class that does the output.

    But the paraphernalia static void main(String[] args) remains; and the object’s existence is so fleeting that students may still not get OO from seeing this version.

    I suggest that instructors actually use applets instead of applications when teaching introductory Java. While not as explicit as the instantation using |new| in Westfall’s version, instantiation of an applet class is done via HTML. A Web browser’s graphic presentation of the applet object clarifies to students that an object is present; and multiple instantiation could make instantiation even more clear. For example:

    • import java.awt.*;
    • import java.applet.Applet;
    • public class HelloWorld extends Applet {
    • public void paint(Graphics g) {
    • g.drawString("hello, world", 10, 20);
  • }
  • }
    • <html>
    • <applet code="HelloWorld.class" width=100 height=50></applet>
    • <p>
    • another instantiation:
    • <p>
    • <applet code="HelloWorld.class" width=100 height=50></applet>
    • </html>

    Hugh Mcguire
    Santa Barbara, CA

    I read Ralph Westfall’s article with disbelief. To teach OOP, he suggests students should not write "Hello, World" in the simplest possible way, but instead create two classes that use each other to do the same thing.

    I believe he is completely wrong in trying to teach OOP to computer science students by obfuscating "Hello, World."

    The cardinal rule of practical programming is to keep it simple. It is the reason why C supplanted Cobol and Unix supplanted IBM-OS360. It is the reason why Java is a clearer and more elegant programming language than C++. Teaching students to make their code more complicated as a matter of style is as bad as teaching them to use longer and more Latinate words when they write.

    OOP is only simpler and more concise when you are dealing with a complex program.

    The proper way to instill a respect for OOP is to give the students two long and complicated programs that need bug fixes and feature improvements. Make the students repair the bugs and run the program with the necessary fixes.

    For the first example, you give them a long and complex non-OO program. For the second example, you give them an OO program. Allow the students to fix either program to get the grade.

    These sorts of exercises will not only teach the students to respect the clarity of OO code, it will also teach students to read code. Reading code is far more useful to a newbie professional than writing code; professionals almost never write code from scratch, but take some sort of existing example and mutate it into a new program.

    Gil Colgate
    Foster City, CA

    Many practitioners recommend programming to an interface, not an implementation (see Design Patterns by Gamma, Helm, Johnson, and Vlissides). This concept is certainly important to learn early on. Here is the revised code

    • interface HelloWorld {
    • public void printHello();
  • }
  • class HelloWorldImpl implements HelloWorld {
    • public void printHello() {
    • System.out.println("hello, world");
  • }
  • class UseHello {
    • public static void main (String[] args) {
    • HelloWorld myHello = new HelloWorldImpl();
    • myHello.printHello();
  • }
  • }
  • Mark Lutton
    Brookline, NH

    Ralph Westfall claims that initial confrontation with the traditional "Hello, World" program prevents students from developing object think; he therefore rewrites the program so there now is one class that defines a printHello method and another class that instantiates the first and calls its method. This transformation is meant to emphasize the difference of object-oriented and procedural programming. But the resulting code, twice as long as the original and technically questionable (why instantiate a class with a static method and no instance variable?), is far from illustrating any merits of OOP since the object in question is nothing but a superfluous wrapping around a single procedure that could be called right away. The whole effort is like describing methods of eating with knife, fork, and plate and then choosing a banana to illustrate the rules.

    Programming education is not about "imprinting" proprieties and manners on students’ minds; we have to explain how to analyze problems and use adequate abstractions to solve them. I never heard convincing reasons for the claim that experience in procedural programming hinders the mastering of OOP. More probably, programmers who do not understand and practice OOP either need better teaching and more time to study or just do not care to learn.

    It may also be the OOP community, despite the wealth of good resources, contributes to the problem. The occasional thoughtless opposition against "procedural think" seems a point in question: Procedures and functions are important programming concepts, and they reappear as constituents of objects in OOP. The difficult question is the adequacy of such concepts, such as what kind of structuring befits a given situation or solution strategy. We must address this subject in our teaching and not simply deprecate the use of procedures and functions as independent program elements. Let’s look at Java for examples. Java does not have standalone procedures and functions. But there is a demand for main programs, for utilities, math-functions, and so on. Java offers them in the guise of static methods packaged into classes whose only purpose is to hold them.

    Or one may need higher order functions (functions that have functions for return values or arguments, the classic example is differentiation or integration in calculus), so a Java programmer is forced to "objectify" functional results and parameters. Indeed: if objects and classes are the essential concept, why use them in such a meaningless and illogical manner? Yes, there may be sound, pragmatic reasons that constrain a programming language designer. But, as teachers of programming courses, we should not take the resulting workarounds for lacking features as a norm and, as in the "Hello,World" improvement here, present the mere packaging of something into one more class as the very idea of OOP.

    Werner Pohlmann
    Salzburg, Austria

    I do appreciate Westfall’s "Technical Opinion" column because I also teach Java. Nevertheless, I’d like to correct his example of HelloWorld class. The method printHello should not have the static access modifier. For this example to work, this static modifier should be omitted. If this method was static, then, one should have called this method:

    • HelloWorld.printHello();

    using the class name as prefix instead of an object name.

    Regarding the essence of the "Hello, World" example, I do think it is quite natural to start learning any programming language (C, or Pascal, Prolog, FORTH, Java) with the "Hello, World" example. In this way, students will feel the flavor of the new language better because, no matter the triviality of the example, it looks so different if written in different languages.

    Most students already have some general idea of OOP before they start learning Java. So, the "Hello, World" example written in Java shows them how different Java’s approach is. One should put all the staff into a class (object nature), then explicitly indicate the main method as "public" and "static" in this class (a more precise use of access modifiers than in other languages), and should follow the template of the main method signature (using design templates to enable appropriate behavior which students understand later when they learn JavaBeans).

    I think the object of this example is to demonstrate to students that writing a Java application is very simple. However, they should have some of Java’s specifics (like executable classes with the main method, or proper use of printing into standard output) in mind from the very beginning.

    The second example in a Java lecture should be Java’s simple class, with some fields and methods, a constructor, a "toString" method, and a subclass like "Point" and "Pixel" classes in James Gosling’s book. I believe this is a better illustration of the nature of OOP (including inheritance) than the object-based rather than object-oriented approach specified by Westfall.

    Vladimir Safonov
    St. Petersburg, Russia

    Westfall Responds:
    I was pleasantly surprised by the number of email messages I received in response to my column. The most common criticism was my use of a static method in the "Hello, World" support class. The problem is that, although the example does work with an instantiated object, it can actually be used without creating an instance. Thus, my example does not do as good a job as it could in demonstrating the fundamental differences between OO and procedural programming. One respondent said this might be a symptom of my lengthy experience with procedural languages.

    I received the following delightful code sample from Shengyang Zhong, a software engineer who sent the very first response.

    • public class SayHello {
    • public static void main(String[] args) {
    • Mouth mouth = new Mouth();
    • mouth.say("Hello, world");
  • }
  • }
  • class Mouth {
    • public void say(String what) {
    • System.out.println(what);
  • }
  • }
  • In addition to avoiding the static support class problem, this code cleverly demonstrates—in the same amount of code—another point that I try to communicate in my programming classes: Objects frequently correspond to things in the real world. Another advantage is that it has the data in the application class and the output method in the support class, thus demonstrating the division of labor aspects of OO programming. I immediately posted this example to my Java class Web site and showed it to my students.

    I forwarded Zhong’s code to many of the respondents, noting its usefulness for demonstrating the advantages of the OO paradigm. Mark Lutton sent me this thoughtful follow-up:

    The correspondence between objects (or computer programs in general) and things in the real world is not static. Thirty years ago, a computer was accessed through a Teletype that had a printer that literally printed the results. Today Java displays the results on a monitor, but the keyword is still "println." (And we still "dial" our touch-tone phones.)

    So we have an implementation of a "mouth" that uses "println" to "say" something, with the result being that characters are drawn on a screen—working with computers can be a very strange experience!

    I was intrigued by Becker’s sixth criterion: "Students should not need to master both using objects and writing classes at the same time." This corresponds with my own experiences teaching Java as a first programming language. Learning to program is learning to map familiar concepts into an abstract representation, which many students find very difficult to do. Requiring them to create support classes while they are initially learning to do this mapping makes the process even more abstract, perhaps 50% more. The time required for them to learn to create their own support objects is time taken away from learning other fundamental aspects of programming. I would greatly appreciate hearing of any books—suitable for CIS students rather than CS students—that initially emphasize use of predefined classes.

    In regard to some of the other criticisms, I would like to reemphasize that "Hello, World" is an introductory example for the first day of class. It is only intended to expose students to a few important aspects of OOP while they are working out the mechanics of a getting Java programs to run. It is not intended to be the basis for the whole course or even several weeks of instruction. If anyone can provide me an example that accomplishes these two objectives better than Zhong’s code, in less than 10 statements, I will be very happy to use it in my Java classes.

    Back to Top

    The Emperor’s Wardrobe

    Henry Ledgard ("The Emperor with No Clothes," Oct. 2001, p. 126) makes the interesting point that computer science isn’t properly a science, and supposed improvements to software development in recent years are perhaps not all they seem. One response is to admit this, and observe that software is more of an engineering discipline than a science. Do we behave like engineers? Might the fluidity of software requirements and development, coupled with human psychology, make this a near impossibility?

    Imagine a group of civil engineers asked to build a bridge across a large river with boat traffic. They are told to design a drawbridge, but to make sure that it would be possible to triple the number of lanes and convert it to a suspension bridge during its life cycle to accommodate increased automobile and river traffic. They’d have a good laugh. If told the request was serious, they’d say it was just plain crazy. Or perhaps instead, imagine a group of aeronautical engineers asked to build a helicopter that can convert in flight to a high-speed long-range airplane. They’d laugh even louder and … oops, that one was specified and built: the Marine Corps Osprey. Not surprisingly, it was behind schedule, over budget, proven unreliable and difficult to use, and expensive to maintain.

    That description is uncomfortably appropriate for many software projects. Software is expected to solve all the problems: current, future, and even unanticipated. Requirements are complex and fluid. Development schedules are compressed. Yet, we all go along with this enthusiastically. Are we all crazy? Perhaps so, or at least a little neurotic. Joseph Weizenbaum (Computer Power and Human Reason, W.H. Freeman, 1976) identified a psychological condition he termed "compulsive programming." Sufferers develop the sense that they have total control of the computer and so should be able to do absolutely anything if they just try hard enough and are clever enough.

    Top-down structured development was an improvement over the spaghetti code of an earlier time, but there were problems, such as difficulty of software reuse and redundant low-level primitive operations. OOP offered well-regulated tools to solve some of this by reintroducing a measure of bottom-up design to complement the weakness of strict top-down methods. Had this been used in moderation, specifically to solve the weakness in the former methods, the results should have been quite good. Were they?

    There was an interesting discovery made when automakers started adding antilocking brakes to cars to increase safety by avoiding skids. Accident rates were expected to drop and insurance discounts were offered at first. Human psychology intervened. Drivers learned they could go a bit faster, drive a bit closer, and the new brakes would keep them from greater danger. In effect, this negated the value of the new technology. OOP may have suffered a similar fate. Since there were new tools to make code safer, programmers did not have to try as hard to develop simple designs and felt able to deal with ever more fluid program development. The new power of OOP was negated by human psychology.

    And now OOP is going to be supplemented with aspect-oriented programming (AOP) which is a new set of tools for dealing with some problems that have begun to be identified in OOP methods. Most of the examples of so-called crosscutting concerns cited in the October 2001 special section in Communications are issues that were once relegated to the operating system: security, synchronization, scheduling, and so on. These issues are being pulled into the application domain by ever more fluid requirements. The OS doesn’t do quite what is wanted. The compulsion to be able to control everything blinds us to the engineering insanity of attacking all these issues at the application level.

    Will the cycle be repeated? AOP offers new tools that seem to solve some of the current problems, and if these tools were used for just these purposes, perhaps it would be a good thing. But once the tools are there, they will be used more than might have been required if the code had been designed a little more carefully. Complexity will rise faster and the myth of control will lead us to go a bit faster and follow a bit closer.

    Something tells me that Ledgard will write another article, in 10 years or so, which says essentially the same thing, but about AOP, rather than OOP.

    Gary Fostel
    Portage MI

    Henry Ledgard’s observations jibe well with my own experience. I spent some time in Silicon Valley working for a company that employed about 300 software professionals. I organized a Friday afternoon seminar called "For Programmers Only" where we discussed this very phenomenon. All the young programmers and most of the older programmers (over 30) believed in OO, but no one could get any significant production done in it.

    The mainstay of our work was Perl, which allowed us to implement the coarse-grained parallelism necessary to deal with the tsunami of genomic data being produced in the private and public domain. We had a few algorithms that had to run very fast and we implemented those mainly in C with a few in C++. That was in the heart of what we called "production bioinformatics."

    I lost count of the millions of dollars thrown at Java- and CORBA-infatuated consultants and companies. I did convince a vice president to try an experiment. We gave a Java and OO consultant ($150/hour) a contract to reimplement a small set of tasks we were already doing in Perl. One of the core problems in this suite was to extract data from the output of another program that comes in human-readable form. After six weeks he had nothing working. The Perl that handled the tasks was about 400 lines and had been written in one week by a very good programmer.

    OO cognitive dissonance being what it is in the Valley, the consultant was kept on and put on other projects that likewise failed or moved along in a crippled fashion.

    Bradley K. Sherman
    Berkeley, CA

    The emperor not only has no clothes, but he rides in a carriage with square wheels. I’ve found almost no ability among computer scientists to confront these issues for many years.

    My research has been focused on reducing the complexity of computer applications (and other technical description) through improved language, especially at the level of data model design. There is a need for accurate comparisons between different representations of programs, but only to choose the simplest, and that does not require numerical quantification of complexity. As excess complexity approaches zero, the differences stand out and the comparisons become easier.

    There is a $25K incentive on my Web site (www.ultranet.com/~eslowry) for computer scientists to understand some implications of minimizing complexity (and in that way maximizing productivity). If anyone is facing these issues, I would like to hear about it.

    Ed Lowry
    Bedford, MA

    Back to Top

    Targeting Zeppelins

    I have used three techniques to reduce project cycle time: Perform adequate needs analysis and design to reduce rework; reduce project scope to deliver less functionality to fewer users; and break projects into independent modules so developers can work in parallel without interference. A fourth method—lavishing resources on an army of coders—is applicable to a single large software company with very wide revenue streams.

    Phillip Armour’s "The Business of Software" column in October ("Zeppelins and Jet Planes: A Metaphor for Modern Software Projects," p. 13), much like recent opinion, asserts without proof that we "cannot estimate projects," and "cannot lock down requirements." If we say from the start that these things are impossible, we excuse ourselves from substantial mental effort. We simply point our trendy, modern project at the sky and blast off in a blaze of coding. We may even feel mentally superior to the antiquated, linear way of doing things that experienced practitioners suggest. Unanswered is the question: Do such projects hit the target any more frequently?

    Determining user needs is difficult work. It takes skill, insight, and analysis. Further, it exposes our own early design ideas to rigorous testing, often finding them disconcertingly faulty. It requires communication with users, which we find less comfortable than coding. How much easier our lives are if we assert that we cannot gain by this effort.

    Scheduling is also difficult work. It involves predicting the future, then betting our reputation on the prediction. Then we must compromise with management, whose bias is always that projects need to take less time than that. How much less stressful it is to make no commitment at all.

    Rather than asserting impossibility, we might acquire and use improved tools and methods for needs analysis and scheduling. We could then point our project directly at a clear, visible target from the moment of launch. This approach is conservative and laborious, not trendy and postmodern. But my personal experience is it is predictably successful when honestly attempted.

    Kurt Guntheroth
    Seattle, WA

    Back to Top

    Join the Discussion (0)

    Become a Member or Sign In to Post a Comment

    The Latest from CACM

    Shape the Future of Computing

    ACM encourages its members to take a direct hand in shaping the future of the association. There are more ways than ever to get involved.

    Get Involved

    Communications of the ACM (CACM) is now a fully Open Access publication.

    By opening CACM to the world, we hope to increase engagement among the broader computer science community and encourage non-members to discover the rich resources ACM has to offer.

    Learn More