CPSC 120: Tutorials on Method Definition

 

Home | Calendar | Announcements | Assignments | Example Code | Notes

Method Definitions: A Pair of Tutorial Lessons

Tutorial #1: Abstraction and Method Definitions

"Abstraction" refers to the practice of removing irrelevant details from a task, in order to create something that can be reused in many contexts. You benefit from this every time you use a microwave (without comprehending the physics of dielectric heating), start a car (with no consideration of automotive construction), or shampoo your hair (regardless of what you know about shampoo chemistry).

In programming, you are used to this from the Processing library methods used in drawing sketches. For example, you use the ellipse() method, by providing four numbers: the (x,y) coordinates, the width, and the height, in that order. You are comfortable with the idea that when you use

ellipse(250,176, 99, 65);

a corresponding ellipse is drawn in the program's window. At no point do you need to think about how this ellipse is drawn: the details of the ellipse() method are totally irrelevant to the task of actually using the method.

Method definition uses another kind of abstraction: the values of the parameters. For example, let's say we have this sketch:

void setup() {
  size(800, 800); 
}

void draw() {
    background(255);
    drawRedRect(mouseX,mouseY,mouseX + 100, mouseY + 90);
}

Within draw(), we don't need to know anything about the definition of drawRedRect(): the only thing we need to know is that the four values specify the top left and bottom right corners of a red rectangle that is drawn in the window.

On the other hand, in the definition of drawRedRect, the fact that we're using it in the draw() method with the mouseX and mouseY values (plus two others) is irrelevant! It is enough to give names to those four values and trust that the values are filled in every time the method runs. This time, the abstraction is over the values that will be provided by users of the method, and it's what we do every time we define a method with parameters:

void drawRedRect(int xa, int ya, int xb, int yb) {
  noStroke();
  fill(255, 0, 0);
  rectMode(CORNERS);
  rect(xa, ya, xb, yb);
}

Combine this definition with the setup() and draw() methods above, and try out the resulting program!

Tutorial #2: Using abstraction in method definitions

Consider this simple program:

void setup() {
  size(500, 500);
  background(127);

  rectMode(CORNERS);
  stroke(0);
  fill(255);
  rect(100, 200, 350, 400);
  noStroke();
  fill(255,0,0);
  rect(135,220,315,380);
}

This draws a white rectangle with a proportionally-sized red rectangle inside of it: the sides begin 35 pixels in from the white rectangle, while the top and bottom are 20 pixels in. Why these numbers? Because 35 is 1/10 of the difference between the leftmost and rightmost x-coordinates of the white rectangle, while 20 is 1/10 the difference between the top and bottom edges. This picture should make the pattern clear:

Now let's rewrite this program in a way that captures the pattern:

void setup() {
  size(500, 500);
  background(127);

  int x1 = 100, y1 = 200, x2 = 350, y2 = 400;
  
  float wid = x2 - x1, hgt = y2 - y1;
  float xoffset = wid/10.0;
  float yoffset = hgt/10.0;
  
  rectMode(CORNERS);
  stroke(0);
  fill(255);
  rect(x1, y1, x2, y2);
  noStroke();
  fill(255,0,0);
  rect(x1+xoffset,y1+yoffset,x2-xoffset,y2-yoffset);
}

This does exactly the same thing as the first example, but now it's easy to change the location and size of the rectangle. If, for example, we want to draw this from (55,60) to (490, 130), all we need to do is change the values assigned to the pair of (x,y) coordinates:

  int x1 = 55, y1 = 60, x2 = 490, y2 = 130;

Try it!

We now have a chunk of code that is the same every time and a portion that changes only in the initial values it assigns to variables. This is exactly what happens when you build a method definition:

void drawPicture(int x1, int y1, int x2, int y2) {
  float wid = x2 - x1, hgt = y2 - y1;
  float xoffset = wid/10.0;
  float yoffset = hgt/10.0;
  
  rectMode(CORNERS);
  stroke(0);
  fill(255);
  rect(x1, y1, x2, y2);
  noStroke();
  fill(255,0,0);
  rect(x1+xoffset,y1+yoffset,x2-xoffset,y2-yoffset); 
}

Only now, we have something far, far more flexible:

int xleft;
int ytop;

void setup() {
  size(500, 500);
  background(127);
}

void draw() {  }

void mousePressed() {
  noFill();
  xleft = mouseX;
  ytop = mouseY;
}

void mouseDragged() {
  stroke(255);
  rectMode(CORNERS);
  rect(xleft, ytop, mouseX, mouseY);
}

void mouseReleased() {
  drawPicture(xleft, ytop, mouseX, mouseY);
}

Combine this code with the drawPicture() definition in a single file. It's a neat effect.


John H. E. Lasseter