Refactoring into Functions

Note: Most of the content of this page is inside toggles. Click on the triangles to disclose the code.

The initial program
function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);

	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);

	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}
image
Use “ablation” to reverse-engineer code
function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// fill("white");
	// circle(85, 200, 80);
	// fill("blue");
	// circle(85, 200, 80-30);
	// fill("black");
	// circle(85, 200, 15);

	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);

	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}

function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// left eye
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);

	// fill("white");
	// circle(185, 200, 80);
	// fill("blue");
	// circle(185, 200, 80-30);
	// fill("black");
	// circle(185, 200, 30);

	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}

function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// left eye
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);

	// right eye
	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);

	// fill("purple");
	// rect(55, 310, 180, 20);
	// circle(55, 305, 60);
	// circle(55+170, 305, 60);
}

image
image
Capture our knowledge about the program into comments
function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// left eye
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);

	// right eye
	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);

	// mouth
	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}
“Factor out” the code stanzas into functions
function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// left eye
	leftEye();

	// right eye
	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);

	// mouth
	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}

function leftEye() {
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);
}
function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// left eye
	leftEye();

	// right eye
	rightEye();

	// mouth
	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}

function leftEye() {
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);
}

function rightEye() {
	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);
}
function setup() {
	createCanvas(windowWidth, windowHeight);
	background("pink");
}

function draw() {
	// left eye
	leftEye();

	// right eye
	rightEye();

	// mouth
	mouth();
}

function leftEye() {
	fill("white");
	circle(85, 200, 80);
	fill("blue");
	circle(85, 200, 80-30);
	fill("black");
	circle(85, 200, 15);
}

function rightEye() {
	fill("white");
	circle(185, 200, 80);
	fill("blue");
	circle(185, 200, 80-30);
	fill("black");
	circle(185, 200, 30);
}

function mouth() {
	fill("purple");
	rect(55, 310, 180, 20);
	circle(55, 305, 60);
	circle(55+170, 305, 60);
}
Now we don't need the comments. The function names are “self-documenting
function draw() {
	leftEye();
	rightEye();
	mouth();
}
This only works because we are using descriptive function names
function draw() {
	f1(); // draw the left eye
	f2(); // draw the right eye
	f3(); // draw the mouth
}

function f1() {
	// …
}

function f2() {
	// …
}

function f3() {
	// …
}
Replace repeated constants by variables
function leftEye() {
	let xCenter = 85;
	let yCenter = 200;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, 15);
}

function rightEye() {
	let xCenter = 185;
	let yCenter = 200;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, 30);
}
Turn the let variables into function parameters, that receive their values from where they are used
function draw() {
	leftEye(85, 200);
	rightEye();
	mouth();
}

function leftEye(xCenter, yCenter) {
	// let xCenter = 85;
	// let yCenter = 200;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, 15);
}
function draw() {
	leftEye(85, 200);
	rightEye(185, 200);
	mouth();
}

function leftEye(xCenter, yCenter) {
	// let xCenter = 85;
	// let yCenter = 200;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, 15);
}

function rightEye(xCenter, yCenter) {
	// let xCenter = 185;
	// let yCenter = 200;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, 30);
}
The remaining difference between leftEye and rightEye is the pupil size. Parameterize it too, so that the functions look the same
function draw() {
	leftEye(85, 200, 15);
	rightEye(185, 200, 30);
	mouth();
}

function leftEye(xCenter, yCenter, pupilSize) {
	// let pupilSize = 15;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, pupilSize);
}

function rightEye(xCenter, yCenter, pupilSize) {
	// let pupilSize = 30;
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, pupilSize);
}
Now leftEye and rightEye have the exact same definition. We could delete rightEye and use leftEye twice
function draw() {
	leftEye(85, 200, 15);
	leftEye(185, 200, 30);
	mouth();
}

function leftEye(xCenter, yCenter, pupilSize) {
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, pupilSize);
}
Since leftEye is used to draw both eyes, rename it to eye
function draw() {
	eye(85, 200, 15);
	eye(185, 200, 30);
	mouth();
}

function eye(xCenter, yCenter, pupilSize) {
	fill("white");
	circle(xCenter, yCenter, 80);
	fill("blue");
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, pupilSize);
}
Now it's easy to add a third eye, by calling eye a third time
function draw() {
	eye(85, 200, 15);
	eye(185, 200, 30);

	eye((185 + 85) / 2, 200 - 50, 5);

	mouth();
}
image
Parameterize the iris color. This allows us to draw the third eye in a different color.
function draw() {
	eye(85, 200, 15, 'blue');
	eye(185, 200, 30, 'blue');

	eye((185 + 85) / 2, 200 - 50, 5, 'green');

	mouth();
}

function eye(xCenter, yCenter, pupilSize, irisColor) {
	fill("white");
	circle(xCenter, yCenter, 80);
	fill(irisColor);
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, pupilSize);
}
image
(Optional material, not shown in class) It's difficult to remember the order of the function parameters. Here's an alternative, that names them where the function is used as well as where it's defined.
function draw() {
	eye({ xCenter: 85, yCenter: 200, pupilSize: 15, irisColor: 'blue' });
	eye({ xCenter: 185, yCenter: 200, pupilSize: 30, irisColor: 'blue' });

	eye({ xCenter: (185 + 85) / 2, yCenter: 200 - 50, pupilSize: 5, irisColor: 'green' });

	mouth();
}

function eye({ xCenter, yCenter, pupilSize, irisColor }) {
	fill("white");
	circle(xCenter, yCenter, 80);
	fill(irisColor);
	circle(xCenter, yCenter, 80-30);
	fill("black");
	circle(xCenter, yCenter, pupilSize);
}

©2020–2022 by Oliver Steele.