Learn Object-Oriented Java the Hard Way

Exercise 5: Programming Paradigms

Before I get too far into the weeds of object-oriented programming (OOP), it might be useful to see the difference between OOP-style code and doing the same program in other programming paradigms.

I created a short program that does four things:

  1. Allow the human to enter a message.
  2. Reverse the order of the characters in the message.
  3. “Camel-case” each word. That is, convert “Hello how are you” to “HelloHowAreYou”.
  4. Display the result.

First, here’s the program using as much of a simple, monolithic style as Java will allow. You don’t have to type this program in unless you really want to.

StringFunMonolith.java
 1 import java.util.Scanner;
 2 
 3 public class StringFunMonolith {
 4     public static void main( String[] args ) {
 5         Scanner keyboard = new Scanner(System.in);
 6 
 7         // input it
 8         System.out.print("Enter a message: ");
 9         String msg = keyboard.nextLine();
10 
11         // reverse it
12         String rev = "";
13         for ( int i=msg.length()-1; i>=0; i-- )
14             rev += msg.substring(i,i+1);
15 
16         // camel-case it
17         String lower = rev.toLowerCase();
18         String[] words = lower.split(" ");
19         String result = "";
20         for ( String w : words )
21             result += w.substring(0,1).toUpperCase() + w.substring(1);
22 
23         // display it
24         System.out.println(result);
25     }
26 }

What You Should See

So, lines 8-9 input the message, lines 12-14 reverse it, lines 17-21 camel-case it, and line 24 displays it. Don’t worry too much if you don’t understand the details of the camel-case part.

Next, I have coded the same program in a “functional” style. Functional style uses only functions with a few inputs and only one output each. The functions don’t share information with each other except through their inputs and outputs.

Again, there’s no sense typing up this version unless you want the practice. (It does work, though.)

StringFunFunctional.java
 1 import java.util.Scanner;
 2 
 3 public class StringFunFunctional {
 4     public static void main( String[] args ) {
 5         Scanner keyboard = new Scanner(System.in);
 6 
 7         // input it
 8         System.out.print("Enter a message: ");
 9         String msg = keyboard.nextLine();
10 
11         // reverse it
12         msg = reverse(msg);
13 
14         // camel-case it
15         msg = camelCase(msg);
16 
17         // display it
18         System.out.println(msg);
19     }
20 
21     public static String reverse( String s ) {
22         String rev = "";
23         for ( int i=s.length()-1; i>=0; i-- )
24             rev += s.substring(i,i+1);
25 
26         return rev;
27     }
28 
29     public static String camelCase( String s ) {
30         String[] words = s.toLowerCase().split(" ");
31         String result = "";
32         for ( String w : words )
33             result += w.substring(0,1).toUpperCase() + w.substring(1);
34 
35         return result;
36     }
37 }

Lines 1-9 are the same as the previous version, because it’s kind of hard to get input from the human in Java any other way.

But you can see on line 12, the message (in the variable msg) is passed in to a function called reverse, and the result is put back into msg, overwriting the previous value. This is a bit more understandable.

And lines 21-27 are the reverse() function itself. It’s the same code as lines 12-14 of the previous assignment, but there’s a little extra setup to name the function and name the parameter and also an extra line to “return” the final result. Notice, though, that we get to call the input s instead of having to care that it’s really called msg elsewhere. It’s a bit nice to be able to call that variable whatever we want without caring what happens in other parts of the program.

Line 15 is the camelCase function call, and lines 29 through 36 are the function definition. Notice that on line 29 we were free to call the parameter s without caring about other parts of the program.

Is the variable in main() really called s?
Doesn’t matter.
Is some other function already using a variable called s?
Doesn’t matter.
What variable is the return value going into?
It doesn’t matter. We can call it result or rev or whatever suits us.

Another nice thing about a functional style of programming is that since each function receives an input and returns an output, functions can be chained very compactly.

Here is the same functional version, but with the functions all nested inside each other.

StringFunFunctionalShort.java
 1 import java.util.Scanner;
 2 
 3 public class StringFunFunctionalShort {
 4     public static void main( String[] args ) {
 5         Scanner keyboard = new Scanner(System.in);
 6 
 7         System.out.print("Enter a message: ");
 8         System.out.println(camelCase(reverse(keyboard.nextLine())));
 9     }
10 
11     public static String reverse( String s ) {
12         String rev = "";
13         for ( int i=s.length()-1; i>=0; i-- )
14             rev += s.substring(i,i+1);
15 
16         return rev;
17     }
18 
19     public static String camelCase( String s ) {
20         String[] words = s.toLowerCase().split(" ");
21         String result = "";
22         for ( String w : words )
23             result += w.substring(0,1).toUpperCase() + w.substring(1);
24 
25         return result;
26     }
27 }

You read line 8 from the inside out. The inner-most thing happens first: keyboard.nextLine() is called. Once it’s done, it returns a String, which we pass immediately to reverse(), then camelCase(), then println().

Some programs are more difficult in a functional style, but when it works it’s really nice and clean-looking.

By the way, formulas in a spreadsheet program like Microsoft’s Excel, Apple’s Numbers or LibreOffice’s Calc are programmed in a functional style. This is a tricky way to code, as you know if you’ve ever struggled to get one right!

Graphics-processing shaders (like in OpenGL or Direct3D) are usually written in a functional style, too, and that makes them well-suited for parallel processing.

Okay, finally let’s do this same little program in an object-oriented style. We’ll use two files as usual: one containing our class/object, and one with the driver. These are the ones you should type in.

StringFunObject.java
 1 public class StringFunObject {
 2 
 3     String message;
 4 
 5     public void setMessage( String s ) {
 6         message = s;
 7     }
 8 
 9     public String getMessage() {
10         return message;
11     }
12 
13     public void reverse() {
14         String rev = "";
15         for ( int i=message.length()-1; i>=0; i-- )
16             rev += message.substring(i,i+1);
17 
18         message = rev;
19     }
20 
21     public void camelCase() {
22         String[] words = message.toLowerCase().split(" ");
23         String result = "";
24         for ( String w : words )
25             result += w.substring(0,1).toUpperCase() + w.substring(1);
26 
27         message = result;
28     }
29 }

There’s something new in this one. On line 3 there’s an instance variable / field, just like you learned about in the previous exercise.

Notice that on line 6, there’s a method that stores a copy of the parameter s into a variable named message. Where is this message declared? It’s the field. We’ll look more at this in later chapters, so don’t worry too much about it for now.

Just remember for this code, any time that “message” is referenced, it’s the instance variable. All the other variables are “local”, which means they only exist inside the method in which they are defined.

So, here’s the code for the driver. Type this one in, too.

StringFunOODriver.java
 1 import java.util.Scanner;
 2 
 3 public class StringFunOODriver {
 4     public static void main( String[] args ) {
 5         Scanner keyboard = new Scanner(System.in);
 6 
 7         // input it
 8         System.out.print("Enter a message: ");
 9         String msg = keyboard.nextLine();
10 
11         StringFunObject sfo = new StringFunObject();
12         sfo.setMessage(msg);
13         sfo.reverse();
14         sfo.camelCase();
15 
16         // display it
17         System.out.println( sfo.getMessage() );
18     }
19 }

What You Should See (Reminder)

(There’s the output again so you don’t have to scroll up for it.)

This is very typical object-oriented code. Line 11 declares and instantiates an object. Line 12 calls the setMessage() method of that object, and passes the message into it as a parameter. Then the next few changes happen inside the object: the message gets reversed, then the message gets camel-cased.

Finally on line 17 of the driver we call the getMessage() method of the object, and it returns to us the modified String for printing.

Maybe you don’t like the object-oriented style. Maybe you think the monolithic version is better, or maybe the functional version.

You know what? I agree with you. Object-oriented programming isn’t a very good fit for a tiny program like this. OOP works best when the programs are large and complicated (like 10,000 lines of code or more).

Whenever I write a program to help me automate something annoying, I almost never code it in an object-oriented style if it’s going to be only 500 lines of code or less.

I write it in a monolithic style if it’s just going to be 10-50 lines long. I use functions when it’s 50-500 lines long, and I start out object-oriented if it’s going to be much bigger than that.

Unfortunately the rest of this book is going to be a little weird. I’m going to use the object-oriented style even for tiny 20-line programs. It’ll be gross. You might not like it. You might think “This program would be so much simpler if he would just….”

But I can’t teach you object-oriented programming using only nice huge 1,000 line perfect examples where OOP makes sense. (Well, I could, but this book would be about 800 pages longer and I wouldn’t have finished writing it yet!) Instead I have to teach you OOP using small silly example programs where the OOP feels weird and forced but the programs are small enough to understand.

Once you’re done with this book, you’re free to code for the rest of your life in a non-object-oriented way. But, if someone dumps a 20,000 (or two-million!) line program on you that uses OOP just to have any hope of preventing bugs, then you’ll have the tools to make sense of it.

Deal?


“Learn Object-Oriented Java the Hard Way” is ©2015–2016 Graham Mitchell