CS 4: Lecture 14
                           Wednesday, March 8, 2006

OBJECT ORIENTED PROGRAMMING (OOP)
=================================
The best way to understand what object-oriented programming is is to contrast
it with "procedural" programming, which existed for decades before.  Object-
oriented programming has everything that procedural programming has, but adds
a way of organizing data and methods around "objects".  Most importantly, it
adds a powerful feature called "inheritance".

Procedural languages have:
- sequential execution, iteration (loops), and selection ("if", "switch"),
- procedure calls (always static--not associated with objects),
- variables,
- structures--repositories of data, like objects and classes.

(Actually, many early procedural languages, from the dark ages of computer
science, don't have procedure calls or structures.)

Object-oriented languages have:
- all the above,
- _classes_ (structure types) and _objects_ (instances of structures),
- _methods_ (procedures) associated with specific classes of objects,
- _access_controls_ like "private",
- and _inheritance_ and _polymorphism_.

Why did the originators of object orientation rename "structures" to "objects"
and "procedures" to "methods"?  Hubris and arrogance, in my opinion.  Some
people believe object orientation is a completely new "paradigm" for
programming.  The best known paradigms for computer languages are these.

- Procedural languages:  Fortran, C, Pascal
- Functional languages:  Lisp, Scheme, Haskell
- Logic languages:  Prolog
- Object-oriented languages:  Java, Smalltalk, C++

The first three of these really use very different ways of thinking about how
to program a computer.  Object-oriented languages are mostly a gloss on
procedural languages.

The one part of object-orientation that really stands out, and might give it
claim to being a separate "paradigm," is _polymorphism_.  A few definitions:

Inheritance:  A class may inherit properties from a more general class.  For
  example, the ShoppingList class inherits from the List class the property of
  storing a sequence of items.
Polymorphism:  The ability to have one method call work on several different
  classes of objects, even if those classes need different implementations of
  the method call.  For example, one line of code might be able to call the
  "addItem" method on _every_ kind of List, even though adding an item to a
  ShoppingList is completely different from adding an item to a ShoppingCart.
Object-Oriented:  Each object knows its own class and how objects in that class
  are manipulated.  Each ShoppingList and each ShoppingCart knows which
  implementation of addItem applies to it.

We'll learn more about inheritance later in the semester.

Let's look again at the other things that separate object-oriented programming
from procedural programming:  classes, methods, and access controls.  These
ideas don't give you any truly new abilities, but they encourage you to use
good programming style and software engineering.

In a procedural language, you can place procedures anywhere in a program.  In a
large program, it is wise to organize your procedures--just as you wouldn't put
the chapters of a book in a random order--but the only tool for organizing them
is self-discipline.

Object-oriented languages encourage you to put each method in the class that it
operates on.  This makes your program easier to maintain and likely to have
fewer bugs.  It also encourages you to use access controls to enforce
information hiding.

Example:  A Rational Number Class
---------------------------------
The problem with floating-point numbers is that they get rounded off, because a
computer can store only a finite number of decimal places.  Sometimes, you want
to be able to do math exactly, even while using division operations.  People do
that by expressing numbers as fractions.  The set of numbers expressible as
fractions is called the _rational_numbers_.  We'll define a class for fractions
in a file named Fraction.java.

public class Fraction {
  private long numerator;
  private long denominator;

We've declared these private as part of information hiding.  We don't want
other classes to be able to mess with a Fraction except through the official
interface--the public methods for accessing a fraction, like this constructor.

  public Fraction(long n, long d) {
    if (d < 1) {
      System.out.println("Fatal error:  Non-positive denominator.");
      System.exit(0);
    }
    numerator = n; 
    denominator = d;
    reduce();
  }

What is "reduce()"?  It's a method for reducing fractions to their simplest
form.  For example, if you consruct the fraction 3/6, reduce() will reduce it
to 1/2.  We'll look at it later.

Thanks to method overloading, we can have more than one constructor.  It's
convenient to have one for fractions representing integers.

  public Fraction(long n) {
    this(n, 1);
  }

"this" is a special keyword in Java, used to allow one constructor to call a
different constructor.  "this(n, 1)" calls the two-argument constructor,
passing n and 1 as arguments.  There's an important difference between calling
"this(n, 1)" and calling "new Fraction(n, 1)":  if you did the latter, you
would create _another_ Fraction.  We don't want this constructor to create two
Fractions; we just want to reuse code from another constructor on the _same_
Fraction, and that's what "this(n, 1)" does.

Why call the two-argument constructor?  For good software engineering.  By
having this constructor call the other, we have reduced duplicate code--namely,
the error-checking code and fraction reducing code in the first constructor.
The program is shorter, and more importantly, if we later find a bug in the
constructor, we probably only need to fix the first constructor to fix all of
them.  (This principle applies to all methods, not just constructors.)

Warnings:
- "this()" must be the _first_ line of a constructor.  Java won't let a
  constructor call "this()" after doing something else.
- You can't use "this()" in a method that's not a constructor.