22

Advanced Object-Oriented Programming

Do you ever think about things that you do think about?

—Henry Drummond, Inherit the Wind

In this chapter:

 Encapsulation

 Inheritance

 Polymorphism

 Overloading

In Chapter 8, I introduced object-oriented programming (“OOP”). The driving principle of the chapter was the pairing of data and functionality into one single idea, a class. A class is a template and from that template I made instances of objects and stored them in variables and arrays. Although you learned how to write classes and make objects, I did not delve very deeply into the core principles of OOP and explore its advanced features. Now that you are nearing the end of the book (and about to leap into the world of Java in the next chapter), it’s a good time to reflect on the past and take steps toward the future.

Object-oriented programming in Processing and Java is defined by three fundamental concepts: encapsulation, inheritance, and polymorphism. You are familiar with encapsulation already; you just have not formalized your understanding of the concept and used the terminology. Inheritance and polymorphism are completely new concepts I will cover in this chapter. (At the end of this chapter, I will also take a quick look at method overloading, which allows objects to have more than one way of calling the constructor.)

22-1 Encapsulation

To understand encapsulation, let’s return to the example of a Car class. And let’s take that example out into the world and think about a real-life Car object, operated by a real-life driver: you. It’s a nice summer day and you’re tired of programming and opt to head to the beach for the weekend. Traffic permitting, you’re hoping for a nice drive where you will turn the steering wheel a bunch of times, press on the gas and brakes, and fiddle with dial on the radio.

This car that you’re driving is encapsulated. All you have to do to drive is operate the functions: steer(), gas(), brake(), and radio(). Do you know what is under the hood? How the catalytic converter connects to the engine or how the engine connects to the intercooler? What valves and wires and gears and belts do what? Sure, if you’re an experienced auto mechanic you might be able to answer these questions, but the point is you don’t have to in order to drive the car. This is encapsulation.

Encapsulation is defined as hiding the inner workings of an object from the user of that object.

In terms of object-oriented programming, the “inner workings” of an object are the data (variables of that object) and functions. The “user” of the object is you, the programmer, who is making object instances and using them throughout your code.

Now, why is this a good idea? Chapter 8 (and all of the OOP examples throughout the book) emphasized the principles of modularity and reusability. Meaning, if you already figured out how to program a car, why do it over and over again each time you have to make a car? Just organize it all into the Car class, and you have saved yourself a lot of headaches.

Encapsulation goes a step further. OOP does not just help you organize your code, it protects you from making mistakes. If you do not mess with the wiring of the car while you’re driving it, you’re less likely to break the car. Of course, sometimes a car breaks down and you need to fix it, but this requires opening the hood and looking at the code inside the class itself.

Take the following example. Let’s say you’re writing a BankAccount class which has a floating point variable for the bank account balance.

u22-05-9780123944436

You will want to encapsulate that balance and keep it hidden. Why? Let’s say you need to withdraw money from that account. So you subtract $100 from that account.

u22-06-9780123944436

But what if there is a fee to withdraw money? Say, $1.25? You are going to get fired from that bank programming job pretty quickly, having left this detail out. With encapsulation, you would keep your job, having written the following code instead.

u22-07-9780123944436

If the withdraw() function is written correctly, the software will never forget the fee, since it will happen every single time the method is called.

u22-08-9780123944436

Another benefit of this strategy is that if the bank decides to raise the fee to $1.50, it can simply adjust the fee variable inside the BankAccount class and everything will keep working!

Technically speaking, in order to follow the principles of encapsulation, variables inside of a class should never be accessed directly and can only be retrieved with a method. This is why you will often see programmers use a lot of functions known as getters and setters, functions that retrieve or change the value of variables. Here is an example of a Point class with two variables (x and y) that are accessed with getters and setters.

u22-09-9780123944436

If I had a Point object p and I wanted to increment the y value, I would have to do it this way:

u22-10-9780123944436

Although the syntax above is awkward, the advantage here is that you cannot set the y-coordinate greater than the height because of the test in p.setY(), whereas p.y = p.y + 1; doesn’t check that for you. If you wanted to enforce this requirement Java allows you to mark a variable as “private” to make it illegal to access directly (in other words, if you try to, the program will not even run).

u22-11-9780123944436

While formal encapsulation is a core principle of object-oriented programming and can be important when designing large-scale applications programmed by a team of developers, sticking to the letter of the law (as with incrementing the y value of the Point object above) is often rather inconvenient and almost silly for a simple Processing sketch. So, it’s not the end of the world if you make a Point class and access the x and y variables directly. I have done this several times in the examples found in this book.

However, understanding the principle of encapsulation should be a driving force in how you design your objects and manage your code. Any time you start to expose the inner workings of an object outside of the class itself, you should ask yourself: Is this necessary? Could this code go inside a function inside the class? In the end, you will be a happier programmer and keep that job at the bank.

22-2 Inheritance

Inheritance, the second in my list of three fundamental object-oriented programming concepts, allows you to create new classes that are based on existing classes.

Let’s take a look at the world of animals: dogs, cats, monkeys, pandas, wombats, and sea nettles. Arbitrarily, let’s begin by programming a Dog class. A Dog object will have an age variable (an integer), as well as eat(), sleep(), and bark() functions.

u22-12-9780123944436

Finished with dogs, I can now move on to cats.

u22-13a-9780123944436u22-13b-9780123944436

Sadly, as I move onto fish, horses, koala bears, and lemurs, this process will become rather tedious as I rewrite the same code over and over again. What if, instead, I could develop a generic Animal class to describe any type of animal? After all, all animals eat and sleep. I could then say the following:

 A dog is an animal and has all the properties of animals and can do all the things animals can do. In addition, a dog can bark.

 A cat is an animal and has all the properties of animals and can do all the things animals can do. In addition, a cat can meow.

Inheritance allows me to program just this. With inheritance, classes can inherit properties (variables) and functionality (methods) from other classes. The Dog class is a child (a.k.a. a subclass) of the Animal class. Children inherit all variables and functions automatically from their parent (a.k.a. superclass). Children can also include additional variables and functions not found in the parent. Inheritance follows a tree-structure (much like a phylogenetic “tree of life”). Dogs can inherit from mammals which inherit from animals, and so on. See Figure 22-1.

f22-01-9780123944436

Figure 22-1

Here is how the syntax works with inheritance.

u22-14a-9780123944436u22-14b-9780123944436

The following new terms have been introduced:

 extends — This keyword is used to indicate a parent class for the class being defined. Note that classes can only extend one class. However, classes can extend classes that extend other classes, that is, Dog extends Animal, Terrier extends Dog. Everything is inherited all the way down the line.

 super() — Super calls the constructor in the parent class. In other words, whatever you do in the parent constructor, do so in the child constructor as well. Processing will call super() for you in most cases, however, I typically leave it in the code to be more clear about what is happening. Other code can be written into the constructor in addition to super(), but super() must come first.

A subclass can be expanded to include additional functions and properties beyond what is contained in the superclass. For example, let’s assume that a Dog object has a haircolor variable in addition to age, which is set randomly in the constructor. The class would now look like so:

u22-15-9780123944436

Note how the parent constructor is called via super(), setting the age to 0, but the haircolor is set inside the Dog constructor itself. Suppose a Dog object eats differently than a generic Animal object. Parent functions can be overridden by rewriting the function inside the subclass.

u22-16-9780123944436

But what if a dog should eat the same way an animal does, but with some additional functionality? A subclass can both run the code from a parent class and incorporate some custom code.

u22-17-9780123944436

in22-01-9780123944436Exercise 22-1: Continuing with the car example from my discussion of encapsulation, how would you design a system of classes for vehicles (that is, cars, trucks, buses, and motorcycles)? What variables and functions would you include in a parent class? And what would be added or overridden in the child classes? What if you wanted to include planes, trains, and boats in this example as well? Diagram it, modeled after Figure 22-1.

22-3 An inheritance example: shapes

Now that you have had an introduction to the theory of inheritance and its syntax, I can develop a working example in Processing.

A typical example of inheritance involves shapes. Although a bit of a cliché, it’s useful because of its simplicity. I will create a generic Shape class where all Shape objects have an (x,y) location as well as a size, and a function for display. Shapes move around the screen by jiggling randomly.

u22-18-9780123944436

Next, I create a subclass from Shape (let’s call it Square). It will inherit all the instance variables and methods from Shape. I’ll write a new constructor with the name Square and execute the code from the parent class by calling super().

u22-19-9780123944436

Notice that if I call the parent constructor with super(), I must include the required arguments. Also, because I want to display the square onscreen, I override display(). Even though I want the square to jiggle, I do not need to write the jiggle() function since it is inherited.

What if I want a subclass of Shape to include additional functionality? Following is an example of a Circle class that, in addition to extending Shape, contains an instance variable to keep track of color. (Note this is purely to demonstrate this feature of inheritance; it would be more logical to place a color variable in the parent Shape class.) It also expands the jiggle() function to adjust size and incorporates a new function to change color.

u22-20a-9780123944436u22-20b-9780123944436

To demonstrate that inheritance is working, here is a program that makes one Square object and one Circle object. The Shape, Square, and Circle classes are not included again, but are identical to the ones above. See Example 22-1.

Example 22-1

Inheritance

u22-01-9780123944436

Figure 22-2

u22-02-9780123944436

Exercise 22-2

Write a Line class that extends Shape and has variables for the two points of the line. When the line jiggles, move both points. You will not need r for anything in the Line class.

in22-01-9780123944436

class Line _______________ {

 float x2, y2;

 Line(___________,___________,___________,___________) {

  super(_________________);

  x2 = _______;

  y2 = _______;

 }

 void jiggle() {

  ______________________

  ______________________

  ______________________

 }

 void display() {

  stroke(255);

  line(______________________);

 }

}

in22-01-9780123944436Exercise 22-3: Do any of the sketches you have created merit the use of inheritance? Try to find one and revise it.

22-4 Polymorphism

Armed with the concepts of inheritance, I can program a diverse animal kingdom with arrays of dogs, cats, turtles, and kiwis frolicking about.

u22-21-9780123944436

As the day begins, the animals are all pretty hungry and are looking to eat. So it’s off to looping time.

for (int i = 0; i < dogs.length; i++) {

 dogs[i].eat();

}

for (int i = 0; i < cats.length; i++) {

 cats[i].eat();

}

for (int i = 0; i < turtles.length; i++) {

 turtles[i].eat();

}

for (int i = 0; i < kiwis.length; i++) {

 kiwis[i].eat();

}

This works great, but as my world expands to include many more animal species, I am going to get stuck writing a lot of individual loops. Isn’t this unnecessary? After all, the creatures are all animals, and they all like to eat. Why not just have one array of Animal objects and fill it with all different kinds of animals?

u22-22-9780123944436

The ability to treat a Dog object as either a member of the Dog class or the Animal class (its parent) is known as polymorphism, the third tenet of object-oriented programming.

Polymorphism (from the Greek, polymorphos, meaning many forms) refers to the treatment of a single object instance in multiple forms. A dog is certainly a Dog, but since Dog extends Animal, it can also be considered an Animal. In code, I can refer to it both ways.

u22-23-9780123944436

Although the second line of code might initially seem to violate syntax rules, both ways of declaring a Dog object are legal. Even though I declare spot as an Animal, I am really making a Dog object and storing it in the spot variable. And I can safely call all of the Animal methods on spot because the rules of inheritance dictate that a dog can do anything an animal can.

What if the Dog class, however, overrides the eat() function in the Animal class? Even if spot is declared as an Animal, Java will determine that its true identity is that of a Dog and run the appropriate version of the eat() function.

This is particularly useful when you have an array.

Let’s rewrite the shape example from the previous section to include many Circle objects and many Square objects.

u22-24-9780123944436

Polymorphism allows me to simplify the above by just making one array of Shape objects that contains both Circle objects and Square objects. I do not have to worry about which are which, this will all be taken care of for me! (Also, note that the code for the classes has not changed, so I’m not including it here.) See Example 22.2.

Example 22-2

Polymorphism

u22-03-9780123944436

Figure 22-3

u22-04-9780123944436

in22-01-9780123944436Exercise 22-4: Add the Line class you created in Exercise 22-2 on page 497 to Example 22-2. Randomly put circles, squares, and lines in the array. Notice how you barely have to change any code (you should only have to edit setup() above).

in22-01-9780123944436Exercise 22-5: Implement polymorphism in the sketch you made for Exercise 22-3 on page 497.

22-5 Overloading

In Chapter 16, you learned how to create a Capture object in order to read live images from a video camera. If you looked at the Processing reference page (http://www.processing.org/reference/libraries/video/Capture.html), you may have noticed that the Capture constructor can be called with three, four, or five arguments:

Capture(parent, config)

Capture(parent, width, height)

Capture(parent, width, height, fps)

Capture(parent, width, height, name)

Capture(parent, width, height, name, fps)

Functions that can take varying numbers of arguments are actually something you saw all the way back in Chapter 1! fill(), for example, can be called with one number (for grayscale), three (for RGB color), or four (to include alpha transparency).

fill(255);

fill(255, 0, 255);

fill(0, 0, 255, 150);

The ability to define functions with the same name (but different arguments) is known as overloading. With fill(), for example, Processing does not get confused about which definition of fill() to look up; it simply finds the one where the arguments match. A function’s name in combination with its arguments is known as the function’s signature — it’s what makes that function definition unique. Let’s look at an example that demonstrates how overloading can be useful.

Let’s say I have a Fish class. Each Fish object has a location: x and y.

class Fish {

 float x;

 float y;

What if, when I make a Fish object, I sometimes want to make one with a random location and sometimes with a specific location. To make this possible, I could write two different constructors:

u22-25-9780123944436

When you create a Fish object in the main program, you can do so with either constructor:

u22-26-9780123944436

In the discussion above, I’ve focused on the number of arguments (along with its name) as what defines a function. However, the argument’s type is also fundamental to its signature. You could, for example, write a third Fish constructor with only two arguments if one of those arguments were, say, a string.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.6.75