Welcome!
Everything is fine.
OOP Inheritance¶
Today doesn't have a lot of code but has some important concepts.
Plan¶
- Announcements
- Recap OOP
- Answer your questions
- Demo Jelly Blobs
- (optional) Chaining
- 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
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:
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¶
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:
- Abstraction: which gives us freedom of implementation
- Polymorphism: which allows us to support common APIs and "distribute" the implementation of methods over a set of objects.
- Inheritance: which allows us to reuse behavior, code and types.