Welcome!
Everything is fine.

OOP Inheritance

Today doesn't have a lot of code but has some important concepts.

Plan

  1. Announcements
  2. Recap OOP
  3. Answer your questions
  4. Demo Jelly Blobs
  5. (optional) Chaining
  6. Breakout

Announcements

  • I'll assign Jelly Blobs partners on Friday

Recap OOP

OOP has the following advantages:

  • Abstraction
  • Polymorphism
  • Inheritance

Abstraction

Discuss: Why is abstraction an advantage?

Polymorphism

Originally created decades ago in the context of a simulation system, but also important in many other situations.

Consider addition. We can define add on many types (classes) of objects:

  • integers
  • floats
  • ratios
  • complex numbers
  • vectors
  • matrices
  • functions
  • many others

Consider this API from coffeerun:

  • add (an order)
  • get (a particular order)
  • getAll
  • remove (an order)

By using abstraction and polymorphism, we can have modules that support the same API.

  • save to the current browser tab
  • save to the browser's local storage
  • save to a cloud-based server

How to Implement classes using Inheritance

In JavaScript, we have two syntaxes:

  • Old syntax, with the prototype chain
  • New syntax, with the class syntax

I'll skip the old syntax, but it's still useful to know about the Prototype chain. Feel free to ask if you're curious.

The Prototype Chain

Here's a diagram of the prototype chain for our example of Rectangles, Circles, and Shapes:

prototype chain for Rectangles and Circles both of which inherit from Shape, which inherits from Object

prototype chain for Rectangles and Circles, both of which inherit from Shape, which inherits from Object

blue ovals are constructor functions

green boxes are prototype objects

pink circles are instances

New ES6 syntax

The new syntax is much easier.

Using the new syntax, we could define Shape like this:

class Shape {
    constructor(color) {
        this.color = color;
    };

    getColor() { return this.color; };

    setColor(color) { this.color = color; };

    toString() { 
        return "[A "+this.color+" "+this.constructor.name+"]";
    };
}

Then, using the new syntax, we could define Rectangle as an extension like this:

class Rectangle extends Shape {
    constructor(color,c1,c2) {
        super(color);
        ...
    };

    ...
}

Here's the full prior example, using the new syntax:

shapes new

There are also cool features to create getters/setters. See Inheritance

The super Function

Each class should take care of initializing its instances. So, Shape should initialize all shapes, and Rectangle should initialize all rectangles. But rectangles are also shapes, so the constructor for Rectangle needs to give the constructor for Shape an opportunity to do its thing.

Hence, the constructor for Rectangle can invoke super(), a function that is the constructor for the parent class, which in this case is Shape. (Before the new syntax, there were complex uses of .call().)

The super() function should be invoked with whatever arguments it needs. Here, the constructor for Shape needs one argument, a color. So the Rectangle constructor, which gets three arguments (color and two corners), invokes super(color), only one argument.

Your Questions

I'll answer your questions

Demo Jelly Blobs

Jelly Blobs

Chaining

If there's time, I'll describe how to implement chaining, like jQuery does. The idea is very simple: just return the object. Here's a Point object with methods to set the x and y coordinate, but allowing chaining.

class Point {
    constructor(x,y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return "Point("+this.x+","+this.y+")"
    }

    getX() { return this.x }

    getY() { return this.y }

    setX(x) {
        this.x = x;
        return this;  // allows chaining
    }

    setY(y) {
        this.y = y;
        return this; // allows chaining
    }
}

var p1 = new Point(3,4);
console.log(p1.toString());

p1.setX(5).setY(7);

console.log(p1.toString());

The chaining code:

p1.setX(5).setY(7);

works because p1.setX(5) returns p1, so the expression becomes p1.setY(7) when it's half-way evaluated.

This page includes that code, so you can try it out.

Breakouts

You can

  • work on Quizzes
  • work on the following Shapes exercise

Breakout Exercise

Copy the shapes code to your account:

cd ~/public_html/cs204/
cp -r ~cs204/pub/downloads/shapes shapes/
cd shapes

Implement a Square class to the system. Create an instance and experiment with it. Here's my solution:

Did you make your Square inherit from Shape or from Rectangle?

class Square extends Rectangle {
    constructor(color, upperLeftCorner, side) {
        let ul = upperLeftCorner;
        let lr = {x: ul.x+side, y: ul.y+side};
        super(color,ul,lr);
    }
}

We can test it like this:

// A 3x3 square with upper left at p1 = (10,20)
sq1 = new Square("yellow",p1,3);
sq1.toString();
sq1.area();
sq1 instanceof Square;
sq1 instanceof Rectangle;
sq1 instanceof Shape;

Summary

Classes allow us:

  1. Abstraction: which gives us freedom of implementation
  2. Polymorphism: which allows us to support common APIs and "distribute" the implementation of methods over a set of objects.
  3. Inheritance: which allows us to reuse behavior, code and types.

appendix about the old syntax